Four Kitchens
Insights

Making WordPress more performant with Pantheon

7 Min. ReadDevelopment

We recently rebuilt and refined the design of our own website. Moving from two separate systems in Jekyll and Drupal to all content being in WordPress. We approached this overhaul in stages. Previously, we’ve written about the benefits of WordPress and when to use component-based content.

This post addresses performance and take a look at the tools we’ve used to make our site more performant.

Identifying performance issues

There are plenty of web-based tools to help identify performance improvement areas. We’ve used the following:

When we started addressing performance improvements our scores were poor — in the “d” grade or 60% performant depending on the individual service’s scoring evaluation method. The commonality found within each is fairly simple on paper but can be challenging in practice. Some server situations are less-than-ideal in addressing several areas of improvement so concessions need to be made. The scoring serves as a guideline and to establish awareness over being viewed as being 100% attainable recommendations.

Commonality in areas for improvement:

  1. Reduce the number of http requests and overall request size by minifying and concatenating your styles & scripts.
  2. Leverage browser caching and content delivery networks to increase server response times.
  3. Optimize images to reduce file size and deliver sizes relative to what the browser needs delivered.

Solving performance issues

In our case our website is built using WordPress and runs on Pantheon servers. With those constraints in mind, here is how we improved our performance.

Reduce the number of http requests and overall request size by minifying and concatenating your styles & scripts

When one is trying to attain this goal using a CMS the method for reducing http requests will differ vastly. In WordPress’ case, one needs to first understand how WordPress enqueues styles and scripts. After that, one must take a look at whether any specific plugins are enqueuing and hooking into core WordPress functionality in a proper manner.

The first step is to identify quick wins: what styles and scripts do we not need at all that WordPress is loading?

Often times this looks something like:

  • Parent theme styles and scripts (TwentySixteen / TwentySeventeen)
  • Plugin offenders: Jetpack loads a ton of potential cruft. In our case, Jetpack and a few other plugins loaded assets we don’t use or need.
  • WordPress core cruft: like loading genericons fonts and emoji support (which only matters if you use WordPress commenting or care about emojis in content).

Sometimes dequeuing a plugin isn’t as easy as defining a function to dequeue it in your functions.php file. To make this easier, I recommend the Asset Queue Manager Plugin. It’s straightforward in how it surfaces all styles & scripts, free to use, and provides one-click disabling. Another similar paid alternative is the Gonzales Plugin. Both work very well for dequeuing assets and allowing your functions file to be that much cleaner.

Minify and concatenate assets

There are so many plugins that offer a broad range of caching/minifying/concatenation functionality. Some are free and work very well while others can be purchased inexpensively.

I’ve used and recommend trying WordPress plugins Autoptimize and Merge + Minify + Refresh.

Leverage browser caching and content delivery networks to increase server response times

In our case, Pantheon offers their Global CDN service. It provides most of the caching-related improvements (CDN for static assets, setting expire headers) out-of-the-box.

Under Apache, I’ve used and can recommend the plugins WP Rocket and W3 Total Cache.

Optimize images to reduce file size and deliver sizes relative to what the browser needs to be delivered

Optimizing images can be divided into two buckets. One for optimizing the images themselves for file size and quality. Another for delivering the best version of each image to the browser. Luckily, WordPress core does the heavy lifting of the latter via srcset.

Image optimization services & plugins

Like any functionality in WordPress land, there are plugins for that. Going further than the plugin, there are services for that. One could manually optimize all images they upload prior to uploading; using an application like ImageOptim but that isn’t always ideal or feasible.

We researched image optimization services and landed on using the Imagify service with the WordPress plugin. I am very happy with the overall experience and recommend it as a low-cost ongoing optimization service that can grow with your website.

In the free arena, there are several options, but none give you the same level of optimization without paying. All “free” plugins are a facade in that they will get you to pay if you want to optimize all your images. It’s wonderful that they all give you enough of a taste for free to decide whether the paid service will work for your needs.

Leveraging srcset to deliver your images

WordPress implemented srcset functionality by default since Version 4.4. One can rely on its core implementation and defined srcset sizes, but extending the functionality may be necessary depending on your own content modeling need.

For our purposes, we needed a helper function to establish consistency in image delivery and to extend core functionality to a specific plugin. The plugin in question being the popular Advanced Custom Fields (ACF) Pro . ACF doesn’t make use of WordPress’ srcset out of the box, so one needs to use a helper function to make use of each image size and allow the browser to make the best decision of which image size to render.

Defining custom sizes

In my experience, serving up custom image sizes (using add_image_size) with the purpose of using them in srcset is best approached through defining width settings over ratio.

Meaning, defining new image sizes by width only is better than trying to maintain a specific aspect ratio in WordPress itself. A designer can uphold specific image ratios better than any CMS can. To me, it makes more sense to impose that structure in the asset creation stage than upon asset upload.

Our sizes look something like this:

//
// Custom image sizes to use throughout our theme
if ( function_exists( 'add_theme_support' ) ) {
add_theme_support( 'post-thumbnails' );


  // additional image sizes - ratio based
  add_image_size( 'one-one-small', 300, 300, true ); //300 square pixels cropped
  add_image_size( 'one-one-medium', 600, 600, true ); //600 square pixels cropped
  add_image_size( 'one-one-large', 800, 800, true ); //800 square pixels cropped

  // additional image sizes - defined width only
  add_image_size( 'three-hundred', 300); // 300 width
  add_image_size( 'five-hundred', 500); // 500 width
  add_image_size( 'six-eighty', 680); // 680 width
  add_image_size( 'seven-fifty', 750); // 750 width
  add_image_size( 'eight-sixty', 860); // 860 width
  add_image_size( 'nine-hundred', 900); // 900 width
  add_image_size( 'nine-fourty', 940); // 940 width
  add_image_size( 'ten-twenty-eight', 1028); // 1028 width
  add_image_size( 'twelve-hundred', 1200); // 1200 width
  add_image_size( 'sixteen-hundred', 1600); // 1600 width
}

Responsive image helper function for ACF (or any custom image field)

ACF gives you 3 options regarding the value one can render. This is set at the field level. Prior to using this helper function, I used all 3 inconsistently. Part of my own refactoring involved making use of ACF’s field rendering values consistent everywhere they’re used.

For the examples below, I render all ACF image field values using their ID.

//
// Responsive Image Helper Function for ACF
// Thanks to Aaron Rutley for his article
// http://aaronrutley.com/responsive-images-in-wordpress-with-acf/
// @param string $image_id the id of the image (from ACF)
// @param string $image_size the size of the thumbnail image or custom image size
// @param string $max_width the max width this image will be shown to build the sizes attribute
//


function fourkitchens_responsive_image($image_id,$image_size,$max_width){

  // check the image ID is not blank
  if($image_id != '') {

    // set the default src image size
    $image_src = wp_get_attachment_image_url( $image_id, $image_size );

    // set the srcset with various image sizes
    $image_srcset = wp_get_attachment_image_srcset( $image_id, $image_size );

    // generate the markup for the responsive image
    echo 'src="'.$image_src.'" srcset="'.$image_srcset.'" sizes="(max-width: '.$max_width.') 100vw, '.$max_width.'"';
    }
}

Using the helper function in your theme

As a standard, I declared all my variables at the top of each template file. This way I can see what images are used at-a-glance and provides a sense of consistency between templates. As mentioned above, all image field values are returned as an ID over URL or Array.

As an example, this is our single-blog.php template header area.

<?php
/**
 * custom srcset featured image
 *
 */
  // get the featured image by defining some variables to print below.
  $blog_featured_image = get_post_thumbnail_id();
  $blog_featured_image_size = 'nine-fourty'; // Set to custom size set in functions.php
  $blog_featured_image_alt = get_post_meta($blog_featured_image, '_wp_attachment_image_alt', true);
?>

Which then gets passed on to the actual meat of the markup like so:

<?php if( !empty($blog_featured_image) ): ?>
  <figure <?php post_class('featured-image medium');?>>
    <img <?php fourkitchens_responsive_image($blog_featured_image, $blog_featured_image_size,'940px'); ?><?php echo 'alt="' . $blog_featured_image_alt . '"';?> />
  </figure>
<?php endif; ?>

This way, each template’s image can be set to a maximum width, allowing the browser with a maximum width to serve by default. There may be a better/cleaner approach to accomplish what I’ve outlined here, but this is serving us well so far.

Conclusions and iteration

It’s not too difficult to profoundly increase the performance of your WordPress website. Using some free and paid plugins can expedite the process, however, you may opt to roll with your own custom plugin instead. This path is more time-consuming in the short term, but potentially time-saving in the long run — community plugins are frequently abandoned or contain poor code quality.

We look forward to leveraging the above enhancements and more in our own website, continually improving performance and WordPress client partnerships. Is your WordPress site suffering from performance issues? Let’s talk!


Resources that helped me throughout this process