Home Insights How to build a fast WordPress website?
28th July 2019 in Web Development

How to build a fast WordPress website?

Louise Towler

By Louise Towler

How to build a fast WordPress website?

In March 2019, Louise delivered a 10 minute lightening talk about how to build a fast WordPress website at the WordPress London Meetup.

Towards the end of 2018 we were asked to build a website for A Place in the Sun Currency to generate leads.  The client asked us to use WordPress, with the Gravity Forms plugin to capture the enquiries and also integrate a currency conversion feed and reviews feed using third-party APIs.

A PLace In The Sun Currency

Making the website fast

Basic good practice to make the custom theme fast on our client’s hosting:

  • Hosting – php 7
  • CSS/JS – minified using Gulp tasks within the theme, custom CSS and a custom grid to minimise code.
  • Minifiy HTML – using a plugin Minify HTML
  • GZIP – added to the .htaccess file
  • Cache control headers – added to the .htaccess file
  • Caching – using a plugin WP Super Cache
  • Image Optimisation – using a plugin TinyPNG

Lazy Loading CSS

Lazy loading non critical CSS in the theme also speeds up the page load time. We styled page components in individual files and then setup a critical CSS and non-critical CSS file importing in specific components.

We did not inline the CSS as it was faster not to for repeat visitors, and the files would not have been cached as the visitor moved between pages.

Third-Party Feeds

The normal way to include a feed on a website page is when the page loads – go and get the rates and reviews using their API and cache the results.

The problem with this approach is that the first person gets a slow page load.  Also, if there is a lot of traffic and two people land on the page at the same time this can cause problems.

Our approach was to create the cached results using a cron job running every 6 hours.  So the results are not triggered by visitors to the website.

The rates request API returns JSON which is stored as a transient in the database.  We store the reviews in the same way as well.

So when a visitor lands on the page, a query is run from the database.  This is faster than a query from the API.

“srcset” Background Images

We used the Advanced Custom Fields plugin in the page editor to setup background images on each page.  To deliver the correct sized background images in the visitor’s browser we added various image sizes:

add_image_size('w-1850', 1850, null, true);
add_image_size('w-1200', 1200, null, true);
add_image_size('w-992', 992, null, true);
add_image_size('w-768', 768, null, true);
add_image_size('w-380', 380, null, true);
add_image_size('w-240', 240, null, true);
add_image_size('w-175', 175, null, true);

Then wrote a function to output the correct image size:

function get_css_background_image($class, $src, $width = null)
    if (is_null($width)) {
        return "{$class} {background-image:url('{$src}')}\n";
    return "@media screen and (min-width:{$width}px){{$class} {background-image:url('{$src}')}}\n";

function acf_image_to_background_images($class, $image, $tags = false)
    $images = [];
    $sizes = $image['sizes'];

    foreach ($sizes as $size => $data) {
        if (1 === preg_match("/^w\-([0-9]{2,4})$/", $size, $matches)) {
            $images[] = ['size' => $matches[1], 'src' => $data];

    usort($images, function ($a, $b) {
        $a = $a['size']; $b = $b['size'];
        if ($a == $b) {
            return 0;
        return ($a < $b) ? -1 : 1;

    $count = count($images);
    $return = [];

    if ($tags && $count) {
        $return[] = '<style>';
    foreach ($images as $k => $image) {
        if (0 === $k) {
            $return[] = get_css_background_image($class, $images[$k]['src']);
        } else {
            $return[] = get_css_background_image($class, $images[$k]['src'], $images[$k-1]['size']);
    if ($tags && $count) {
        $return[] = '</style>';

    return implode('', $return);

Which can the be used like this:

<?php if ($image = get_field('reviews_image')) : ?>
    <style><?= acf_image_to_background_images('.reviews', $image); ?></style>
<?php endif; ?>

This function outputs the following:

@media screen and (min-width:175px){
@media screen and (min-width:240px){
@media screen and (min-width:380px){
@media screen and (min-width:768px){
@media screen and (min-width:992px){
@media screen and (min-width:1200px){

Loading Gravity Forms in the Footer

When the Gravity Forms CSS and JavaScript is loaded in the website header this is render blocking.  We decided to speed up the website by loading the CSS and JavaScript for Gravity Forms into the website footer.

The problem is that Gravity Forms outputs JQuery to run straight after the form code embedded on the website page.  But if you load the CSS and JavaScript in the footer when you pass the page the Gravity Forms code does not exist.  So then the JavaScript throws errors and will not run.

Our solution was to add a Javascript event listener which runs Gravity Forms when the page is fully loaded.

add_filter('gform_init_scripts_footer', '__return_true');

add_action('wp_enqueue_scripts', function() {
    if (function_exists('gravity_form_enqueue_scripts')) {

add_filter('gform_cdata_open', function ($content = '') {
    return 'document.addEventListener("DOMContentLoaded", function() {';

add_filter('gform_cdata_close', function ($content = '') {
    return '}, false );';

So now the code is in the footer it is not render blocking.  The page loads in 0.5 seconds so although the form is not clickable that is not a problem for the website visitor.

Fast WordPress Website Speed Test Results

We use a number of different programs to test website speed.  Here are the results using GTMetrix:

  • Time To First Byte (TTFB) – 49ms
  • First Paint – 283ms
  • Contentful Paint – 283ms
  • Fully Loaded – 523ms

To find out more about the WordPress London Meetup you can visit the WPLDN website.

If you would like us to build you a fast WordPress website then please get in touch today.

Leave a Reply

Your email address will not be published. Required fields are marked *