A few months ago I documented how I decreased the loading time of a WordPress site in this post. Although those changes made a significant speed improvement the website would still be slow for anybody a long distance from the server as each page had to be grabbed from my origin server instead of being served by Cloudflare CDN (which is where the static page resources are served from).

Over the last 2 months the website has been gaining popularity in countries which are thousands of miles away from the origin server meaning that 500-800 milliseconds wouldn’t be an unusual amount of time to wait for a response from the origin due to the distance the data has to travel. Unfortunately Cloudflare’s Cache Everything mode wouldn’t be viable as that can’t be disabled for users who are logged into the site (on the free plan, at least) and therefore they are served the guest pages instead of the personalised pages after logging in.

Conditional Caching with Cloudflare Workers

Cloudflare Workers allows Javascript code to be run server-side within Cloudflare’s datacenters during the connection between the client and Cloudflare’s regular servers. The code I wrote essentially checks for WordPress cookies and if they’re found then the request is passed through to my origin server as usual, however, if there aren’t any WordPress cookies then Cloudflare’s cache is used. This means that logged out users will get a cached page from their local Cloudflare datacenter and logged in users will be passed straight through to the origin so they get personalised pages.

I used Pingdom Tools to test the speed of the website from Sydney (which is about as far from my origin server as you can be) both with Cloudflare Workers and without. With Workers and caching enabled the page was able to be fetched in 181ms instead of 1305ms for the uncached page; a difference of over one second.


Without Workers enabled; uncached – click to enlarge


With Workers enabled; cached – click to enlarge

The x-cache header is added by my origin and the x-cfw-cache is added by my Javascript Worker.

The code I used is as follows:


//I make no guarantees this code will work reliably. Use at your own risk. Please thoroughly test before deploying.
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event))
})

async function handleRequest(event) {
  let request = event.request;
  let response = null;
  console.log('Got request', request);
  cookies = request.headers.get("Cookie");
  if (cookies && cookies.toLowerCase().includes("wordpress_")){
    console.log("We have cookies. Don't cache.", cookies);
    response = await fetch(request);
    response = new Response(response.body, response);
    response.headers.set("x-cfw-cache", "BYPASS");
    return response;
  }
  let cache = caches.default;
  response = await cache.match(request);
  if (!response){
    console.log("No cache. Will fetch.");
    response = await fetch(request);
    response = new Response(response.body, response);
    let responsecookies = response.headers.get("Set-Cookie");
    if (responsecookies && responsecookies.toLowerCase().includes("wordpress_")){
      //Wordpress cookies are being set here. We don't want to cache.
      response.headers.set("x-cfw-cache", "NO");
    } else {
      
      response.headers.delete("Set-Cookie");
      response.headers.set("x-cfw-cache", "MISS");
      console.log(response.headers.get("Cache-Control"))
      event.waitUntil(cache.put(request, response.clone()))
    }
  } else {
    response = new Response(response.body, response);
    response.headers.set("x-cfw-cache", "HIT");
  }
  
  console.log(response.headers.get("x-cfw-cache"))
  return response
}

Most of my debugging code is still present to make it easier to understand. By enabling Workers on Cloudflare and using the above code, you should get Cloudflare to safely cache your WordPress HTML assuming your origin sends valid public cache headers for HTML pages. Workers is priced based on the amount of requests that pass through them so it’s best to disable them when unneeded; in my configuration I have Cache Everything enabled and Workers disabled for /wp-includes/* and /wp-content/* as those directories only contain static content and can be cached the same for everyone without using Workers – This saves a large amount of requests hitting Workers unnecessarily and will save money for popular sites.

Pingdom has reported my hourly average homepage load time worldwide as 242ms and 252ms for the last 2 hours whereas it was around 510ms to 530ms prior to enabling Workers with caching.

Categories: SoftwareWordpress

Leave a Reply

Your email address will not be published. Required fields are marked *