Cloudflare: enable full page caching with WordPress

Cache the HTML of your webpages on Cloudflare’s CDN for quicker loading sites that use less data transfer and lighten the load on your web server.

Why should I consider caching HTML pages of my site?

Using a Content Delivery Network (CDN) like Cloudflare can offer significant speed improvements to your WordPress site. Rather than every request for a page or asset having to be served by your web server, Cloudflare’s CDN can cache a version of that content, including HTML pages, JavaScript files, stylesheets and images, serving them from a server close to the visitor’s location. That means fewer requests to your origin server resulting in less data transfer, lower resource demands and quicker loading sites.

Cloudflare’s caching rules can be customised through the use of Page Rules. By default, Cloudflare will only cache static assets such as images and script files, but it is possible to implement full page caching on a WordPress site ensuring that a cached version of the webpage itself is also served from Cloudflare’s servers.

WordPress’ default headers prevent full page caching

When it comes to HTML pages, Cloudflare will respect the cache-control header sent with your content. Cache control is a response header sent by the server that specifies how a resource should be cached, where it’s cached and the maximum age it can reach before expiring.

If you check the response headers on a WordPress page, you’ll see the standard cache-control headers look something like this:

cache-control: no-store, no-cache, must-revalidate

This particular cache-control header means:

  • no-store browsers aren’t allowed to cache a response and must request it from the origin server each time it is requested
  • no-cache a browser may cache a response but must first submit a validation request to the origin server
  • must-revalidate once a resource becomes stale (based upon its max-age), browsers and other clients must not use their stale copy without revalidating the request on the origin server

Since a max-age directive is not also included, the no-store directive will refer to the expires header which you should find will be some date in the past. Essentially, the default caching headers provided by WordPress ensure that every page load results in a request being sent to the origin server.

The process of enabling full page caching on Cloudflare for your WordPress site therefore has two steps. First, we need to amend the cache-control headers for the page(s) you wish to cache. Cloudflare will respect the cache-control headers sent from the server so unless we modify these to tell Cloudflare that it’s okay to cache a page, any page rules we setup will be ignored. Once that’s done, we need to add a Page Rule to tell Cloudflare to ‘Cache Everything’ on those pages, not just static assets.

How to implement full page caching on WordPress

1. Amending cache-control headers on WordPress

Before going any further, you will need to determine which pages of your WordPress site you want to cache. You should avoid caching any pages that require a login and there may be other, more dynamic, pages of your site that you wish to serve fresh each time.

If you just want to send cache-control headers for the whole site, you can simply send via a function added to an appropriate WordPress hook. Since we may want to make the sending of the headers conditional upon the post type or other attributes of the current content, we’ve used the template_redirect hook below.

 * Add cache-control headers to response

function add_cache_control_headers() {
    if (!is_admin()) {
        header("Cache-Control: public, must-revalidate, max-age=3600");
add_action( 'template_redirect', 'add_cache_control_headers' );

You’ll notice that we’ve wrapped the headers in an !is_admin() check to make sure they are not returned on the admin pages of the site. If you wish to limit these headers to a specific post type or area of the site, you can extend this if statement with the appropriate conditions. For example, to cache only posts, you could use the following code:

 * Add cache-control headers to response

function add_cache_control_headers() {
    global $post;
    if (!is_admin() && $post->post_type == 'post')
        header("Cache-Control: public, must-revalidate, max-age=3600");
add_action( 'template_redirect', 'add_cache_control_headers' );

When you load the appropriate pages of the site, you should now see the cache-control headers above passed in the response headers. In turn, the directives mean:

  • public a resource can be cached by any cache – an alternative value of private means that a resource can only be cached on a client device, for example a desktop browser but not a CDN
  • max-age determines the amount of time that a resource can be cached before it becomes stale and, since we have the must-revalidate directive, any cache knows that it cannot serve stale content and, after this time has passed (1 hour in our example above), it must request a fresh copy from the origin server

2. Setting Cloudflare Page Rules to Cache Everything

Now that we have amended the cache-control headers for the content we want to cache, we can go about creating the Page Rules we need. This step is straightforward – we just need to tell Cloudflare that for URLs that match a given pattern we want to set the Cache Level to Cache Everything.

Here’s an example which would cache all content within the /blog/ directory, including the /blog/ page itself. Once you’re happy with the rule, click Save and Deploy to send it live.

You should then see your new Page Rule in the list:

Finally, let’s validate that these pages are now indeed being cached and served from Cloudflare’s CDN. If you load one of the affected pages and check the response headers you should see both the cache-control and cf-cache-status headers. The cf-cache-status header tells you whether a version of this page was successfully found in Cloudflare’s CDN cache.

The first time you request a new URL, this may be returned as MISS as it has not yet been stored within Cloudflare’s cache. However, if you reload the page you should now see the header returns a value of HIT, meaning the page is successfully being loaded directly from Cloudflare’s cache.

cache-control: public, max-age=3600, must-revalidate
cf-cache-status: HIT

And that’s all there is to it. You can amend the max-age value as appropriate for your content and roll out page rules that range from targeting specific resource-intensive pages of the site to covering the entire site.

Important: Before you enable full page caching of pages on your WordPress site, it’s crucial to consider whether the content of those pages changes at all when a user is logged or. For example, you may be using the standard WordPress admin bar that appears at the top of the page.

If this is the case, you may need to hide those elements so that the logged in experience is identical to that of unauthenticated users. This is more straightforward, of course, if you have certain logged in pages that you want to omit from caching as you can simply tweak the add_cache_control_headers() function above to exclude these.

A lightweight, intuitive WordPress theme to enable flexible developement.

  • Lighting-fast installer
  • Intuitive SASS structure
  • Bloat-free

Build with Barebones