TL;DR – Caching plugins on their own give you a performance edge compared to a non-cached WordPress site, but this can be taken a step further by leveraging Nginx’s try_files
directive to bypass PHP entirely and serve the cached files directly for even greater performance.
In computing, a cache is a temporary store of data. Typically, the data stored in a cache will be slow or expensive to compute, so the time required to retrieve the item from the cache is less than the time it would take to recompute the data from scratch. This principle can be applied in many ways but is especially prevalent in dynamic websites like those powered by WordPress.
By default, whenever you visit a page on a WordPress site, several things will happen. PHP will process your request, which will query a database to retrieve the dynamic content related to the page being visited. Each step takes time to complete, degrading the user experience through long page load times. Expensive operations can also lead to increased load on the server, and therefore – depending on your hosting provider – additional cost.
However, these wait times can be heavily reduced with a properly configured cache. With caching, the generated document can also be stored so that the next time a user requests this same web page, it can be returned immediately, and the work to fetch the data and create the document can be skipped, significantly cutting down page load time and improving the user experience.
There are several kinds of caching relevant to WordPress websites. However, this article will focus on page caching. You’ll see how different page caching approaches stack up against each other, as well as three popular caching plugins for WordPress and how they can be turbocharged with the help of Nginx.
Table of Contents
- How Nginx Caching Works
- How WordPress Page Caching Plugins Work
- The Best WordPress Cache Plugins
- Why Caching Plugins Perform Poorly in Nginx
- Making WordPress Cache Plugins Fly
- Benchmarks
- Which Plugin is Right for You?
- Final Thoughts
How Nginx Caching Works
If you’ve done much research into different caching options, you’re likely to have come across Nginx FastCGI Caching. Not only is it the caching method we’ve chosen for the SpinupWP platform, but it’s been the subject of many of our other articles on caching, including Varnish vs Nginx FastCGI Cache: Which is Best for WordPress?, where various popular WordPress caching solutions were benchmarked. One thing is clear, Nginx FastCGI Caching is blazingly fast and relatively easy to configure. However, it does have its quirks, such as the difficulty in invalidating the cache.
All services on a Linux server are run as a specific system user. Nginx FastCGI Caching works best when both Nginx and PHP run as the same user. This means you can purge the cache files directly from PHP. Nginx will always create the cache files as the user it’s running under and it’s not possible to adjust this behavior. Similarly, all cached files are created with 0600
or 0700
permissions for files and folders respectively and this too cannot be adjusted. This means that when Nginx and PHP run as separate system users (which is often the case) you can no longer invalidate the cache from PHP. As you’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. You could install and configure one of the commonly used WordPress caching plugins, and this will go a long way in addressing any performance issues. However, if you look at the results from a previous 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
- WordPress 31
- Simple Cache 281
- FastCGI Cache 420
- 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 web 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 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. At this point, the script execution is halted, which prevents WordPress from completing 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 you 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 features and ease of use. The chosen plugins are some of the most popular in the WordPress community, with consistently high ratings for those that are listed in the WordPress Plugin Repository, as well as a track record of excellence when it comes to caching. They offer a variety of options, including a premium, feature-rich plugin, as well as free plugins with varying degrees of additional functionality. Whatever your need, there is a good chance one of these plugins has you covered.
In addition to the plugins we’re looking at here, there are several other options available, including WP Fastest Cache, Simple Cache, LiteSpeed Cache, WP Fastest Cache and Hyper Cache.
1. WP Rocket
WP Rocket is one of the best WordPress caching plugins available. It is a premium plugin that starts at $59 per year 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 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 1 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 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 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 as 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 the try_files
directive in the config 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 that 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_slash_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 previous benchmarks performed, we’ll be using ApacheBench.
ab -n 10000 -c 100 https://hellfish.media/
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 with TLS1.3):
- DigitalOcean 2GB with 2vCPUs ($18/mo)
- Ubuntu 22.04 LTS
- PHP 8.1
- Nginx 1.24.0
- MariaDB 11.1.2
- WordPress 6.3.1, Twenty Twenty-Three
The versions of the plugins used for these benchmarks are:
- WP Rocket – 3.15.1
- WP Super Cache – 1.10.0
- W3 Total Cache – 2.5.0
Benchmark results are affected by numerous factors, from the server resources available and the versions of software and plugins used, to harder-to-replicate factors like local internet speed. As such, actual results may vary over time, but as long as they are reviewed in the context of their original datasets, relative comparisons between different caching methods can still be easily made.
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
- No cache 42
- WP Rocket 253
- WP Super Cache 233
- W3 Total Cache 213
- Nginx FastCGI Cache 287
However, using the Nginx try_files
directive with the caching plugins gives very similar results to using Nginx FastCGI Caching.
Requests Per Second with Nginx try_files Results
- WP Rocket 279
- WP Super Cache 281
- W3 Total Cache 283
- Nginx FastCGI Cache 287
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
- WP Rocket 357
- WP Super Cache 354
- W3 Total Cache 353
- Nginx FastCGI Cache 347
These results make sense when you consider what is happening here. With the updated try_files
directives, Nginx is using the cached version directly in each scenario, bypassing PHP – and therefore the caching plugins – entirely. This means the differences between each of the plugins are largely irrelevant in the case where the HTML page is already cached.
Which Plugin is Right for You?
The three caching plugins examined in these benchmarks all perform similarly once used with the Nginx try_files
directive, but there are some differences between them worth considering if you are trying to decide which one is right for you.
WP Rocket
The most compelling offering in terms of features is WP Rocket. Not only does it have some of the best pre-Nginx benchmark results, it also has an excellent list of features on offer. WP Rocket does caching, but it has the ability to minify CSS and JS, image lazy loading, image optimization (via Imagify, another plugin from the same developers), and CDN support. This makes WP Rocket an excellent one-stop-shop for uplifting your site’s performance, and the SEO benefits that come with that. In terms of pricing, there is no free version of WP Rocket, so if you would rather stick with a free caching plugin, the other plugins have you covered.
W3 Total Cache
W3 Total Cache comes in second place in terms of functionality, offering the standard page caching you’d expect, as well as database, object, and browser caching, as well as CDN functionality. As a free plugin, this is excellent value if you’re after something a bit more advanced than basic page caching.
WP Super Cache
WP Super Cache may not have as many additional features as the other two plugins, but it focuses on page caching, and does it very well. Super Cache offers a high degree of configurability for your page cache, but comes with reasonable defaults that you’re unlikely to need to change in most cases. This makes WP Super Cache a great choice if you already have other plugins for things like image optimization and are looking for a solid, no-nonsense page caching solution.
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.