Top 3 WordPress Cache Plugins and How to Turbocharge Them with Nginx

Anyone trying to improve the performance of their WordPress site eventually turns to caching. There are a ton of WordPress page caching plugins available, but limiting yourself to caching plugins alone means leaving significant performance improvements on the table.

If you’ve been reading the Delicious Brains blog for any amount of time, you’ll likely know that I’m a massive advocate of Nginx FastCGI Caching. Not only is it the caching method we’ve chosen for the SpinupWP platform and our server setup guide, but it’s been the subject of many other blog posts. Most recently, Varnish vs Nginx FastCGI Cache: Which is Best for WordPress?, where I benchmarked popular WordPress caching solutions. One thing is clear, Nginx FastCGI Caching is blazingly fast and relatively easy to configure. However, it does have its quirks. Namely, the difficulty with invalidating the cache.

All services on a Linux server are run as a specific system user. Nginx FastCGI Caching works great when both Nginx and PHP run as the same user. This is because the files cached by Nginx are writable by PHP, meaning you can purge the cache directly from PHP. This is particularly important when publishing or updating a post in WordPress. However, Nginx will always create the cache files as the user it’s running under and it’s impossible to configure the owner or group of those files. Similarly, all cached files are created with 0600 or 0700 permissions for files and folders respectively and this cannot be customized. This means that when Nginx and PHP run as separate system users (which is often the case) the wheels quickly fall off as you can no longer invalidate the cache from PHP.

As we’re unable to customize the file permissions in Nginx, the solution is to have PHP create the cache files instead. This brings us to WordPress caching plugins. However, if you look at the results from our last set of benchmarks, you’ll see that Simple Cache (a WordPress caching plugin) was no match for the performance offered by Nginx FastCGI Caching.

Requests Per Second Comparison Results

  1. WordPress 31
  2. Simple Cache 281
  3. FastCGI Cache 420
  4. Varnish 337

Why is that? Let’s dig into how page caching plugins work.

How WordPress Page Caching Plugins Work

By far the biggest bottleneck in WordPress is connecting to the database server and querying the data required to respond to the current request. This is done on every single request, regardless of whether the data has changed between requests. This is extremely inefficient.

WordPress page caching plugins alleviate this issue by generating a static HTML version of the request, which is usually saved to the filesystem. Subsequent requests serve the static HTML file instead of querying the database and re-rendering the page. To do this, page caching plugins make use of the advanced-cache.php drop-in, which is created when the plugin is installed and executed before WordPress is loaded (if the WP_CACHE constant is true). WordPress core handles this in wp-settings.php:

if ( WP_CACHE && apply_filters( 'enable_loading_advanced_cache_dropin', true ) && file_exists( WP_CONTENT_DIR . '/advanced-cache.php' ) ) {
    // For an advanced caching plugin to use. Uses a static drop-in because you would only want one.
    include WP_CONTENT_DIR . '/advanced-cache.php';

    // Re-initialize any hooks added manually by advanced-cache.php.
    if ( $wp_filter ) {
        $wp_filter = WP_Hook::build_preinitialized_hooks( $wp_filter );
    }
}

Most caching plugins have a lot of additional logic which is triggered from advanced-cache.php but it usually boils down to a simple file_exists check, which checks for the existence of a static HTML file. If the file exists, its contents are read and written to the output buffer. The script is then exited, which prevents WordPress from executing the rest of the request. It’s worth noting that anything hooked into the shutdown hook will still be executed.

A very simple example might look like this:

if ( @file_exists( $path ) && @is_readable( $path ) ) {
    @readfile( $path );
    exit;
}

The Best WordPress Cache Plugins

Now that we know how caching plugins work, let’s take a look and compare a few of the top plugins. Because the basic page caching implementation is the same across the plugins, generally the performance will be similar as well. So for this comparison, we are focusing on available features and ease of use. In addition to the plugins we’re looking at here, there are several other options available, including: WP Fastest Cache, Simple Cache, Cache Enabler, Comet Cache and Hyper Cache.

1. WP Rocket

WP Rocket

WP Rocket is one of the best WordPress caching plugins available. It is a premium plugin that starts at $49 for a single site. There is no free version, although they do have a 14-day, money-back guarantee if you want to try it out.

Despite being the only paid plugin on this list, WP Rocket consistently ranks on top in terms of features and ease of use. Page caching is enabled automatically after activating the plugin. A simple and straightforward interface allows further optimization and fine-tuning of the cache rules.

WP Rocket also has broad compatibility with other plugins, themes and hosting providers. It plays well with common eCommerce plugins including WooCommerce. WP Rocket is fully compatible with SpinupWP and other hosts that have their own page caching solutions. This means that it will disable its own page caching to avoid conflicts, but you can still use the other features.

A few of the other features included with WP Rocket:

  • CSS and JavaScript file optimization
  • Media lazy loading
  • Cache preloading
  • CDN integration
  • Database optimization

2. WP Super Cache

WP Super Cache

WP Super Cache is a free and open source caching plugin from Automattic, the company behind WordPress.com. It is one of the most popular caching plugins with over 2 million active installations.

Although WP Super Cache doesn’t have quite as many extra features, the page caching works well and the user interface is simple and straightforward. In Easy mode, you can enable page caching with a single switch. Advanced mode gives you more control to customize your cache settings.

Some of the other features with WP Super Cache:

  • Garbage collection to clean out stale cache files
  • Cache preloading
  • CDN integration

3. W3 Total Cache

W3 Total Cache

W3 Total Cache is another popular free plugin, with over 1 million active installations. There are also paid options for premium support, as well as a pro version with some additional features.

Although W3 Total Cache has a ton of options to optimize your setup, it’s not quite as easy to use and can be overwhelming for beginners. However, it does include a helpful setup guide to help you get started.

Some of the other features include:

  • HTML, CSS and JavaScript minification
  • Object caching
  • Database caching
  • PHP OPCode caching
  • Image lazy loading
  • Browser caching optimization

Why Caching Plugins Perform Poorly in Nginx

Although WordPress page caching plugins prevent WordPress from loading and significantly improve performance, they don’t quite keep up with Nginx FastCGI Caching. Why is that? Quite simply, it’s because each request is being processed by PHP, which takes far more CPU cycles than just letting Nginx handle the request.

If you remember the good old days when Apache was prevalent, most caching plugins would recommend adding a mod_rewrite rule to your .htaccess file. This would allow Apache to serve the request without ever hitting PHP. This was possible because Apache allowed configurations to be overwritten at runtime using .htaccess files. Nginx, however, doesn’t support such conveniences.

If we could get Nginx to perform a similar file_exists check like in PHP, we could likely speed up the performance of WordPress caching plugins.

Making WordPress Cache Plugins Fly

As it turns out, Nginx does in fact perform a similar file_exists check on every single request it handles. Most Nginx configurations have the following block.

location / {
    try_files $uri $uri/ /index.php?$args;
}

This essentially tells Nginx to serve the requested file if it exists, otherwise, perform an internal redirect to index.php. If we add our cache directory to this block, we can instruct Nginx to serve the cached HTML file directly without ever hitting PHP.

Let’s take W3 Super Cache as an example, which stores its cache files like so:

// Request: hellfish.media/
wp-content/cache/supercache/hellfish.media/index-https.html
// Request: hellfish.media/hello-world/
wp-content/cache/supercache/hellfish.media/hello-world/index-https.html

As you can see the path to the cached index-https.html file in the plugin cache directory always matches the requested URL.

With that in mind, we can update our try_files directive to use the Nginx $http_host and $request_uri` variables, like so:

location / {
    try_files "/wp-content/cache/supercache/$http_host/$request_uri/index-https.html" $uri $uri/ /index.php?$args;
}

After reloading Nginx, any cached pages generated by W3 Super Cache will be served directly from Nginx without ever hitting PHP. Requests which haven’t been cached will continue to be handled by WordPress, which will generate the cache for subsequent requests.

Similarly, for WP Rocket we can use:

location / {
    try_files "/wp-content/cache/wp-rocket/$http_host/$request_uri/index-https.html" $uri $uri/ /index.php?$args;
}

For a more advanced Nginx configuration for WP Rocket, check out the rocket-nginx package.

With W3 Total Cache, if you are using the “Disk: Enhanced” page cache method, you can use:

location / {
    try_files "/wp-content/cache/page_enhanced/$http_host/$request_uri/_index_ssl.html" $uri $uri/ /index.php?$args;
}

Another option for W3 Total Cache is to include the generated nginx.conf file in your site’s Nginx configuration. This file is automatically updated with your W3 Total Cache settings.

Benchmarks

It’s time to benchmark the results! As with the last set of benchmarks performed, I’ll be using ApacheBench.

ab -n 10000 -c 100 https://siteunderload.com/

This simulates 10,000 requests, with a concurrency of 100 requests. Meaning, ApacheBench will send a total of 10,000 requests in batches of 100 at a time. Each test will be performed a total of 10 times and the average used for comparison.

The server stack looks like this (with each site running on HTTPS):

  • DigitalOcean 1GB ($5/mo)
  • Ubuntu 20.04
  • PHP 8.0
  • Nginx 1.20.1
  • MySQL 8.0.25
  • WordPress 5.8, Twenty Twenty-One

Requests Per Second

Let’s start with requests per second, which is the number of concurrent users your server can handle (higher is better). First, let’s benchmark the plugins with their default page cache settings against Nginx FastCGI Cache. As you can see here, the plugins perform similarly against each other, but the FastCGI Cache still offers significantly better performance.

Requests Per Second Default Results

  1. WP Rocket 268
  2. WP Super Cache 272
  3. W3 Total Cache 238
  4. Nginx FastCGI Cache 407

However, using the Nginx try_files directive with the caching plugins gives very similar results to using Nginx FastCGI Caching. And in some cases, it’s even marginally faster.

Requests Per Second with Nginx try_files Results

  1. WP Rocket 412
  2. WP Super Cache 408
  3. W3 Total Cache 404
  4. Nginx FastCGI Cache 407

Average Response Time (ms)

The average response time is the total time it takes for a request to complete (lower is better). This is the average across all 10,000 requests, which simulates the response time when the server is under load. As above, with the Nginx try_files directive the results are very similar to using Nginx FastCGI caching.

Average Response Time Results

  1. WP Rocket 244
  2. WP Super Cache 246
  3. W3 Total Cache 249
  4. Nginx FastCGI Cache 247

Final Thoughts

As expected, using a caching plugin with the Nginx try_files directive performs much better, and very close to the same performance as Nginx’s native FastCGI Caching.

In reality, the difference between Nginx FastCGI Caching and the various plugins with the Nginx try_files directive is negligible and any of these caching solutions are excellent options. If you are using a managed host or SpinupWP, you probably don’t need a plugin for page caching, as these services will have already implemented their own caching solution. Otherwise, I would recommend trying out a few of the different plugins to see which one you prefer.

Have you performed a similar optimization using Nginx and page caching plugins? Let us know in the comments below.

Author

Ashley Rich

Ashley is a Laravel and Vue.js developer with a keen interest in tooling for WordPress hosting, server performance, and security. Before joining Delicious Brains, Ashley served in the Royal Air Force as an ICT Technician.

Start Your 7-Day Free Trial

Start your SpinupWP journey today and spin up your first server within minutes.

Subscribe to get the latest news, updates and optimizations in performance and security.

Thanks for subscribing 👍

To receive awesome stuff, you'll need to head to your inbox and click on the verification link we sent you.
Make sure to check your "spam" folder or your "promotions" tab (if you have Gmail).
If you're still having trouble, then message us at sudo@spinupwp.com.

You are already logged in

It looks like you are already logged in to SpinupWP.

Please log out of this account to continue.

Registration Successful

Thanks for registering for a new
SpinupWP account.

Before getting started, could you verify your email address by clicking on the link we just emailed to you?

SpinupWP

Free Trial

Start Your 7-Day Free Trial

No credit card required. All features included.

By signing up to SpinupWP, you agree to our Terms and Conditions.
For privacy related information, view our Privacy Policy.