A look at WordPress page caching with a CDN

We all know how important it is to have a fast WordPress site. If your site is slow, people leave or don’t make a purchase. It’s also a signal Google uses to do its ranking.

To make our WordPress site fast, we use different tools for different parts of the server stack. We have an object cache to speed up our MySQL queries. And then we have a HTTP cache to speed up how fast we return HTML back to the browser. You can see a diagram of how those different pieces work together below. (You can also read more about it here.)

You might have never heard of a HTTP cache, but you probably heard of the category of plugins that act as a HTTP cache. They’re called page caching plugins. Most WordPress sites use one or if they don’t, it’s because their host handles the HTTP caching. (Most of the time using Varnish.)

Now, you can also add a content delivery network (CDN) to the mix. If you’re not familiar with what a CDN is, it’s a service that lets you cache content in data centers across the globe. This allows you to serve cached content even faster because the CDN will serve it from the data center closest to the person making the request.

Now, a lot of page caching plugins support CDNs. But they only do so for media files such as CSS, JS, images and video. The HTML content that the page caching plugin generates still gets served from the server where your WordPress site is located. This means that, even with page caching, your site can still be slow for someone who isn’t close to where that server is.

This is, in essence, why you should use a CDN to do page caching. So why aren’t more plugins helping you do it? Let’s look at why that is and current solutions surrounding this problem.

The problem: Cache invalidation

One of the most well-known sayings in computer science is:

There are only two hard things in Computer Science: cache invalidation and naming things.

It’s one thing to do page caching with a CDN. It’s another to tell the CDN when you updated a post or a page and the cached version is out of date. Without a plugin to tell the CDN that a page is out of date, it becomes hard to have a website that feels responsive.

Now, page caching plugins could do this. But the reality is that it’s not their responsibility. They do page caching on a server, not with a CDN. It’s really the responsibility of the CDN to create a WordPress plugin that can do page caching for you.

CDN page caching with a plugin: Cloudflare

Speaking of CDNs with a WordPress plugin, the most popular CDN in the WordPress ecosystem is Cloudflare. It has a free tier which a lot of CDNs don’t have. But on top of that, it also has an excellent WordPress plugin.

Until recently, Cloudflare’s plugin behaved like every other CDN plugin. It allowed your CSS, JS, images and video files to get served by Cloudflare, but not the HTML. This all changed when they announced automatic platform optimization (APO).

With APO, you can now have Cloudflare cache your HTML content along with all the media files it handled previously. The only caveat to APO is that it’s not free if you use Cloudflare’s free plan. If you’re using the free plan, APO costs $5/month. That’s still a great deal for the price.

But if you’d rather not pay $5/month, you can look at this plugin. It works on Cloudflare’s free tier and also supports more advanced cache busting scenarios. Worth evaluating if you have more complex use cases which APO might not cover.

Tell the CDN how long to cache a page

What happens if you’re not using Cloudflare? Well, it’s still possible to do page caching with other CDNs. For example, if you use Ymir, it’ll set up CloudFront (Amazon’s CDN) to do page caching by default. (This is why this article loads so quickly.)

But, as we saw, page caching is only part of the problem. You also need to be able to tell the CDN if the content is out of date. If you don’t have a plugin to tell the CDN that, what can you do?

You can tell the CDN how long to keep something in cache using the Cache-Control header. The Cache-Control header will tell the CDN how long it should keep the content cached. Another option is to just configure the CDN itself to keep the HTML content cached a certain amount of time. (This is what Ymir does with CloudFront at this time.)

But how long should you keep that content cached?

Well, that depends on the site and how responsive you need it to be to content changes. By default, Ymir does 5 minutes. This strikes a good balance between responsiveness and caching during traffic surges. But if your site is very static with little to no changes, you can push that up to something extreme like a year. (I’ve done that for my small plugin site.)

Let’s say you don’t want to do this by configuring your CDN. You’d want to use the Cache-Control header. To cache HTML for 5 minutes, you’d need to make sure WordPress returns Cache-Control: max-age=300 as the Cache-Control header. The max-age directive tells the CDN (or Browser) that it can keep the content cached for 300 seconds (5 minutes).

The Future: stale-while-revalidate directive

In 2010, Internet Engineering Task Force (IETF) created a RFC to extend the Cache-Control header. One aspect of the RFC was the addition of the stale-while-revalidate (SWR) directive. This directive is actually the solution to the cache invalidation problem we’ve been talking about.

The stale-while-revalidate works in combination with the max-age directive that we saw earlier. To illustrate, let’s go back to our earlier example with the max-age=300. If we made a request after 5 minutes, we’d have to fetch content again because the cached version is now stale.

But let’s say we changed the Cache-Control header to Cache-Control: max-age=300, stale-while-revalidate=3300. Our new directive still has the max-age which says that if you request content within 5 minutes, it’s still fresh and you can use the cached version. The stale-while-revalidate directive changes what happens after that 5 minute window.

At that point, the cached content is now stale. Before, your browser or CDN would fetch the content again. But now, it’ll look at the stale-while-revalidate directive. If the request came within 55 minutes (3300 seconds) of when the cache content expired, it’ll use the cached content, but request a new cached copy in the background. This gives you a 60 minute window to serve cached content, as shown in the diagram below.

But the advantage with the directive is that you don’t have to worry about telling the CDN when the content is out of date. It’ll check regularly without any need to tell it that the content is out of date. This is especially true if you use a Cache-Control header like Cache-Control: max-age=60, stale-while-revalidate=86340. This will keep the content cached for 24 hours, but any request that’s over a minute old will trigger a cache refresh.

If that sounds amazing, it’s because it is! The issue with stale-while-revalidate header is that it isn’t supported by a majority of CDNs still. And the CDNs that support it don’t have a WordPress plugin that leverages it to do page caching.

Hosting options

Don’t want to deal with configuring a CDN to do page caching? Let’s look at who else supports page caching with a CDN besides Ymir. This isn’t a well documented feature that hosting companies talk about.

In fact, there are only two hosts that promote a feature that does this. First, there’s Servebolt with their accelerated domains feature. They use Cloudflare as their CDN to do it. (You can read more about it in their announcement post.)

There’s also Pagely which does page caching with a CDN. It’s part of their Mercury feature. They use CloudFront to do this like Ymir does.

Besides Servebolt and Pagely, there aren’t any other hosting providers that currently promotes this approach. And both charge a lot for the feature. Pagely is a premium managed host and Servebolt only offers accelerated domains with the enterprise plans.

Update: If you’re looking for something more affordable, another host mentioned to me that does this for all their plans is Rocket.net.

This will get more common

Doing page caching with a CDN isn’t always necessary if your traffic always comes from locations close to the server. (This is why your server location is often important!) But if you have global traffic, using a CDN to do page caching becomes much more important. The faster your browser gets HTML back, the faster it can render the rest of the page.

That’s why I expect other hosting providers to follow Pagely and Servebolt’s initiative. And as the stale-while-revalidate directive more widely supported, we should see caching plugins start leveraging it too. Until then, using a CDN to do page caching will be limited to people hosting on Pagely or Servebolt, using Cloudflare, configuring their CDN themselves or deploying WordPress with a platform like Ymir that supports it.