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

2 Comments

Pairfum London · 6th June 2019 at 12:27 pm

Hi Joseph,

I read your post with great interest.

Do you have an update on this post:
– How well are the Cloudflare workers for you?
– Has your code proven to be solid or did you modify it?
– Where / How did you place your code, in the Cloudflare dashboard or within WordPress functions.php?
– Did you use the special Cloudflare Cache Plugin for workers (https://wordpress.org/plugins/cloudflare-page-cache/)

Would love to hear more about how the workers are ‘working’ for you.

Kind regards,

Pairfum London
https://www.pairfum.com

    Joseph · 6th June 2019 at 2:55 pm

    Hi,

    Thanks for your comment.

    Workers has been working very well for me since I enabled it. I’ve had no issues and everything has been as reliable as expected.

    I made some small modifications to the code as non-200 responses were being cached which meant that temporary issues with the site would be cached and therefore being served to more visitors even after being fixed. The updated code can be found here: https://bit.ly/CfWorker

    All of the code is within the Cloudflare Dashboard and I’m not using any plugins related to Cloudflare. When I first made this code and implemented it, Cloudflare Workers was still a very new feature. Since then, things have changed but I’m not very knowledgable about the new additions to Workers. My code works fine for my situation, but there may now be better ways to do it using the newest features.

Leave a Reply

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