Caching in WordPress is often misunderstood, and websites load slower than they should as a result.
In this guide I hope to clear some of those misconceptions and provide some clarity on the technical issue.
Hopefully, by the end of this guide, you’ll have a better understanding of how the different caching layers play a role in WordPress optimization.
What is cache and why is it important?
A cache is a storage layer that stores temporary copies of data or files from one request, so that future requests can access that content more quickly.
In WordPress, this can mean anything from caching an entire page, to caching the results of a database query.
Before we dive into the different caching mechanisms, it’s essential to understand the benefits of caching.
Caching performs two main functions:
- It improves application performance. In the case of WordPress sites, this means your site loads faster and provides a better user experience.
- Increases application performance. This means your site can handle more traffic.
What’s more, caching can increase application performance without increasing hosting costs. This is because far fewer system resources (CPU and memory) are needed to host a site that has been properly cached.
In addition, better performance can be beneficial for SEO, as page speed is one of the ranking factors used by search engines. This is a win-win strategy (when done correctly).
WordPress is a database-driven CMS, which means there are many moving parts to handle each incoming request.
For example, WordPress has to query the database and process the page before sending it to the user. This happens on every incoming request, which is hugely inefficient if the content of the page hasn’t changed.
A typical request will look something like this:
Generally, the more moving parts involved in handling a request, the longer the user has to wait for a response and the more system resources are used.
To combat this, caching is often done in layers, with each layer sitting in front of a moving part. The three main layers in WordPress are usually broken down into:
- Browser cache
- Page cache
- Object cache
Let’s dive into each of these layers. We’ll work from the outside in, which brings us to the first one, the browser cache.
While browser caching doesn’t necessarily help application response time or performance (at least as far as WordPress is concerned), it is the most important layer when it comes to reducing the amount of data that needs to be sent from the server to the browser.
This can make your site feel much more responsive because static assets like CSS, JS and images appear much faster if they are cached by the browser.
When it comes to understanding browser caching, the network tab in your browser’s developer tools is your best friend. Let’s take a look at the browser cache in action.
The first page load will not have any assets cached by the browser (I have enabled the “Disable cache” option to simulate an initial page load).
The metrics we are interested in are the amount of data transferred and the total resources.
First, note that each metric has two values because you can filter the results by resource type. This allows you to easily see which type of resource is taking up the most bandwidth.
Second, the value transferred should always be less than the total resources, even if it is the first time the browser loads the page (or the “Disable cache” option is enabled), for two reasons:
- Assets that are stored in the browser cache are not transferred.
- Assets such as HTML, CSS and JS must be compressed (using Gzip or Brotli) by the server before being transferred to the browser. They are then decompressed by the browser before being displayed to the user.
Therefore, if the browser cache is correctly configured, subsequent page loads should transfer less data. You can check this for yourself when reloading the page.
The data transferred has dropped from 808 Kb to 108 Kb, which is a big saving. You will also notice that the load metric has dropped from 1.32ms to 1.02ms.
You can configure the browser’s cache behavior using the cache-control and expires headers that your server adds to the response of static assets.
The cache-control header will take precedence over the older expires header. However, both are usually applied to ensure compatibility with some legacy proxy services.
You can check these headers with different tools…
Normally, the cache-control header will have a value of
max-age=seconds which will instruct the browser to cache the file for a maximum of seconds and prevent it from being redownloaded on subsequent requests.
In the above case, the file will be cached by the browser for a maximum of 1 year.
Common browser cache problems
- Cache invalidation is difficult. Once a browser has cached an asset using
cache-control: max-age=secondsyou can’t tell the browser to disable it, at least not without changing the URL. That’s why WordPress adds a version query string to the queued assets. For example:
- Website speed analysis tools, such as PageSpeed or GTMetrix, will display short-lived header expiration warnings. Often these warnings are triggered by external assets such as Google Analytics. This is because external services have to use short expiration times to ensure that their assets are updated, due to difficulties with cache invalidation. With a long expiration time you would have to update your embedded scripts every time Google Analytics changed its code. Don’t forget that the expiration time is very short.
cache-controland expires headers are not the only ones used by the browser to determine whether a resource should be fetched from the server. For example, once the duration specified by
cache-control: max-age=secondshas elapsed, the
etagheader will be used. This contains a validation identifier (similar to an MD5 algorithm of the requested resource), which is compared with the resource identifier stored on the server. If the value of the
etagis the same, the resource returns a “304 Not Modified” response. This tells the browser that the cached resource has not changed and that it must renew the expiration time specified in
cache-control: max-age=seconds. HTTP caching is the subject of a whole separate guide, so I won’t go into it here for now.
How to optimize the browser cache
We can optimize the browser cache to our advantage by postponing the content download update, as we saw in this guide:
On the other hand, if your hosting uses NGINX Direct Delivery, it already optimizes this part for you.
Page caching is going to provide you with the biggest benefit in terms of improving both application response time and performance.
Page caching essentially turns WordPress (a database-driven CMS) into a static HTML site by taking PHP and MySQL out of the equation when it comes to handling a request.
The average response time is five times faster when page caching is enabled. This is quite striking when you consider that 10 times as many requests per second are handled.
Common page cache problems
- Page caching is extremely problematic when sites display custom content such as e-commerce sites or are very dynamic such as forums. Often, the page cache will have to be disabled completely or specific pages like
/checkoutwill have to bypass the cache. This doesn’t happen with WooCommerce as we already saw, but you should keep it in mind.
- Cache override can be tricky depending on how it is implemented. Due to the dynamic nature of WordPress, it is difficult to determine on which pages to clear the cache when updating content (especially when there are files, pagination and widgets involved). Therefore, the method often used is to empty the entire page cache when there are changes to the content.
- Using multiple page cache solutions will not improve performance. In fact, I do not advise you to use multiple page cache solutions, as it can cause cache inconsistencies (different versions of your site stored in each cache) and make it difficult to invalidate the cache.
- Page caching is usually disabled for logged-in users, so object caching is still advisable (more below).
How to optimize page caching
To optimize dynamic queries you can use any of the caching plugins out there, such as WP-Rocket, WP SuperCache and others or, more efficiently, CloudFlare’s APO.
As I mentioned earlier, not all pages can be cached. This is especially important for e-commerce and affiliate websites, which often display custom content.
It also happens with the WordPress admin area. If these dynamic pages were cached, users would see custom content that would not be relevant to them.
WordPress has built-in object caching, which allows data such as database queries to be stored in memory. This way, multiple calls to functions like
get_posts only generate a single database query.
However, the object cache is not persistent by default (meaning it doesn’t live beyond one request).
Luckily, WordPress can integrate with persistent data storage systems such as Memcached or Redis, essential for scaling dynamic pages.
The object cache sits between PHP and the database, which speeds up PHP execution time and reduces database load by storing queries in memory.
You can see the impact an object cache has by installing the Query Monitor plugin.
Loading the WooCommerce cart page without the object cache enabled results in 224 queries to the database:
With the object cache active it drops up to 34 database queries and reduces the page generation time from 4.0886 seconds to 1.3208 seconds:
These tests are done on a SiteGround server with the latest versions of WooCommerce and WordPress, PHP 8.0.3, the Astra theme in a testing site.
Common problems with object caching
- Additional server software such as Redis or Memcached is required to make the object cache persistent. This should be solved if you use a good WordPress hosting provider, or you can install it yourself if you manage your own server.
- Object caching does not completely eliminate database dependency, which is often the biggest bottleneck in WordPress. This is because SQL queries to build the index of entries will always be executed, as the results are not stored in the cache.
How to optimize the object cache
As I mentioned above, you need your hosting to have an object cache management system installed and active, such as Redis or Memcached, unless you have a dedicated server and know how to install and configure them on your own.
These caching systems usually activate the WordPress internal object cache to take advantage of the existing engine and optimize it.
To do this they usually perform two actions:
- Enable object caching in the wp-config.php file with define(
- Create advanced object cache files such as
wp-contentfolder. These files usually appear in your plugin screen as dependent or drop-in plugins.
Caching plugins for WordPress
Popular caching plugins, such as WP Rocket, W3 Total Cache or WP Super Cache, are a common way to add caching features to your site.
However, you may not need to use any plugin at all. You should keep in mind that caching plugins don’t always work as well as server-based caching.
A good WordPress hosting provider will take care of the caching for you.
What about CloudFlare?
If you are using Cloudflare, you may be wondering if different caching layers are still necessary.
This means that when a visitor comes to your site, the cached version of those assets will be downloaded from the CDN server closest to their location. This reduces the loading time of the site assets.
A CDN like Cloudflare can be used in addition to the other caching layers. Although page caching and object caching are still important for optimizing your WordPress site.
Remember that it is possible to configure page rules in Cloudflare for full page caching. However, the option to skip cookie-based caching is only available for Business and Enterprise plans.
The ability to skip cookie-based caching is critical for a WordPress site to avoid serving cached responses to logged-in users, or for other dynamic content such as the shopping cart on an e-commerce website.
Cloudflare offers the automatic optimization platform for WordPress, which can solve these problems at a cost of $5 per month per domain for users of the free plan.
There is even more to learn
Well, I hope you liked this guide, although I’ve only scratched the surface when it comes to caching in WordPress.
That said, I hope you got a better understanding of how the different caching layers work.
If you want to dig deeper, I recommend checking out the HTTP caching resources on Google’s developer website, which offers more information on browser caching.
The WordPress optimization articles of this blog is another excellent resource for learning more about how to improve the performance and loading speed of WordPress sites.
Still have questions about caching in WordPress? Ask in the comments below.