Improve Page Load Time For Your WordPress Site with WP Offload S3: A Case Study

One of the great things about working at Delicious Brains is working on products that I use and love outside of work. I was a WP Migrate DB Pro customer well before joining the team and still use it daily on personal sites and side projects. However, I’ve not often had the need to use our other plugin, WP Offload Media (formerly WP Offload S3) to offload site media files to Amazon S3 and DigitalOcean.

In June, I spent some time working on my wife’s teaching resources website migrating it to a new server, changing the domain and deploying a rebrand. Her focus this year is to grow the site and increase the number of paying members. As part of increasing conversions I want to improve the speed and performance of the site and with so many PDFs and images, WP Offload S3 and the Assets Pull addon are the perfect plugins to use for this kind of optimization.

Why WP Offload S3?

So why would you use the plugins? Offloading your media to Amazon S3 to be served by CloudFront has a number of benefits that come down to backups for peace of mind, saving money, and improving page load times.

If you are copying media to S3 and leaving it on your server, then you are effectively creating a backup of your wp-content/uploads directory making it part of your backup plan. You can also use an option to remove the files from the server after they have been offloaded to S3 to save space on what can be more expensive hosting than the cost of S3 storage.

Serving media and site assets (with the Assets Pull addon) from CloudFront and not your server will reduce the requests to the server, decreasing CPU and IO activity. Because the CloudFront CDN will deliver assets from the nearest location to the user rather than the location of your server, requests should be quicker which makes for a faster page load, a better experience for the user, and improving a metric that Google takes into consideration.

We’re not going to see huge reductions in load time or massive speed increases; however, after you done all the the other performance improvements in this guide, this approach is a relatively simple method to gain valuable further speed improvments when every millisecond counts.

Let’s dive in.

Installation

As the site is managed with Composer, installing both WP Offload S3 and the Assets Pull addon is extremely easy since we introduced dedicated Composer support for our premium plugins.

Once WP Offload S3 is activated you can configure the settings via Settings > Offload S3. I’ve set up my IAM user with restricted access to the bucket in use for the site and defined my access key and secret in my wp-config.php file. I’ve also defined the bucket in the config file to keep these details out of the database.

The Print Play Learn site uses Easy Digital Downloads to manage the PDF downloads and by default all downloads are stored within the wp-content/uploads/edd directory so they can be restricted by htaccess or nginx rules. WP Offload S3 has an integration with EDD to set files to private when they are uploaded to S3. However, when bulk offloading existing media to S3 the download files aren’t set as private, so I used a filter in the plugin to tweak the access level and set the directory on S3 to keep the edd/ path:

function ppl_add_edd_dir_to_path( $args ) {
    if ( false === strpos( $args['SourceFile'], 'uploads/edd/' ) || false !== strpos( $args['Key'], 'uploads/edd/' ) ) {
        return $args;
    }

    $args['Key'] = str_replace( '/uploads/', '/uploads/edd/', $args['Key'] );

    return $args;
}
add_filter( 'as3cf_object_meta', 'ppl_add_edd_dir_to_path', 10, 2 );

function ppl_make_edd_objects_private( $acl, $data, $post_id ) {
    if ( 'private' === $acl ) {
        return $acl;
    }

    $file_path = get_attached_file( $post_id, true );
    if ( false === strpos( $file_path, 'uploads/edd/' ) ) {
        return $acl;
    }

    $parts = explode( '.', $file_path );

    if ( 'pdf' !== $parts[1] ) {
        return $acl;
    }

    return 'private';
}
add_filter( 'as3cf_upload_acl', 'ppl_make_edd_objects_private', 10, 3 );

The above code was added to a file wp-content/mu-plugins/site.php which I use to keep site wide code tweaks in.

Once all of the existing Media Library had been offloaded to S3 and the Copy Files to S3 setting is turned on for future uploads, we want to think about serving the files from S3. Let’s set up a CloudFront distribution so the files aren’t served directly from S3 and we can make use of the benefits of a CDN. These are the best docs to describe setting up a CloudFront distribution and how to use CloudFront with a custom domain on HTTPS, as I want to use cdn.printplaylearn.com not an ugly CloudFront default URL like sdskh43433k4lk.cloudfront.net.

Assets Pull Addon

The final piece of the puzzle is getting all of the site’s assets such as JS, CSS, and image files to also be served by CloudFront. We can use the Assets Pull addon to achieve this and it’s pretty simple to set up. Once the addon is installed and activated you can either go through the helpful setup steps or configure manually.

I want to use the same CloudFront distribution running on the same cdn subdomain as my media library, this has a performance benefit over running two distributions. Having two DNS entries (eg. cdn. for the media and assets. for the assets) means two HTTPS connections, but reducing to one entry with one connection makes use of HTTP/2’s multiplexing asynchronous connection. It also just feels better having all of the assets under the one subdomain.

Take a look at this doc to configure the same distribution for both the media library and the assets of your site.

Considerations

I run a local development copy of the site, a staging site, and of course a production version of the site, but after the initial development and testing I don’t want my dev or staging site to offload test images to S3. We’ve talked about strategies for handling media across environments before, and I’m opting for the ‘no access’ strategy here, with only the production site having the AWS access key and secret configured. No new media on the other environments will be offloaded, but existing media will be served by CloudFront.

I mentioned earlier the ‘Remove Files From Server’ setting, which means local files are removed after offload. In theory this is great, I can remove the thousands of PDFs and associated images from my VPS (so I don’t need to keep increasing the disk space as the site grows) and put them all on S3. However, there are downsides to doing this. Some image manipulation plugins need access to the image file and although the plugin handles this 99% of the time, there is the possibility of running into issues. For me the PDFs are the largest drain on disk space and will never be edited, so I can use one of the plugin’s filters to ensure all other files are left on the server:

function ppl_preserve_non_pdf_files_on_server( $check, $file ) {
    $parts = explode( '.', $file );

    if ( 'pdf' !== $parts[1] ) {
        return true;
    }

    return $check;
}
add_filter( 'as3cf_preserve_file_from_local_removal', 'ppl_preserve_non_pdf_files_on_server', 10, 2 );

The last thing I came across was some console errors when serving the assets from CloudFront, specifically related to web font files in my theme which are subject to Cross-Origin Resource Sharing (CORS). It was resolved with a simple snippet added to my nginx config as explained here.

The Results

Before deploying the plugins I did some testing of the site using a couple of tools to benchmark the results and then compare later.

First of all I used Apache Bench to send 10,000 requests to a page on the site, with a concurrency of 100 requests:

$ ab -n 10000 -c 100 https://printplaylearn.com/resources/

I wanted to see if the CPU usage on the site (predominantly from the nginx process as the site has full paging caching enabled) was reduced after the changes. Here’s the spikes from Digital Ocean’s reporting. You can see the CPU spike was ~5% lower after implementing the plugins:

CPU results comparison after using WP Offload S3

To test if the speed of page loading improved with the plugins I used Sitespeed.io. But because the site is on a DigitalOcean droplet in London, and I’m based in the UK it wouldn’t be a fair test to run it from my machine as requests to the site assets before the plugins would always be quick from a location point of view. So I quickly spun up a new server based in New York to run the tests from. I needed to install Docker on the server and use the Docker method to run the test:

$ docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:7.3.5 https://printplaylearn.com/resources/

After reviewing both results, I found the page load time decreased on average by 0.78 seconds after implementing the plugins, winner!

Site page load results comparison after implementing WP Offload S3

Conclusion

All in all, adding WP Offload S3 and the Assets Pull addon to my wife’s site has made a difference. It has added some small but effective performance gains as well as reducing my hosting costs. And especially compared to some of the other tasks you might undertake to improve your site’s performance, it was generally simple to set up and the filters available made any tweaks I needed easy to implement.

Do you use WP Offload S3 and the Assets Pull addon? What are your go-to optimizations for site performance? Let us know in the comments below.

About the Author

Iain Poulson Product Manager

Iain is a product manager based in the south of England. He also runs multiple WordPress products. He helps people buy and sell WordPress businesses and writes a monthly newsletter about WordPress trends.