Cache All The Things!

This site runs on WordPress, and as you’ve seen from the front page, we recommend Cloudflare. One of Cloudflare’s main features is caching (no, not “cashing,” like money…but it sounds the same).

Normally, Cloudflare caches static content, like images, CSS, and JS files. And that makes them fast! But the page itself, even if nothing changed, isn’t cached for faster performance. Cloudflare would have to get that page from your server, then deliver it to your visitor. This can really slow things down, especially on a WooCommerce site.

You could create a Page Rule to “Cache Everything, but you don’t want your back-end pages cached, or the WP Admin Bar, or Editing links on your pages and posts because you were logged in when the Cloudflare cache refreshed your page. Your visitors will see what you see, and that’s not good.

How can you safely cache “dynamic” content?

If you’re on a Business Plan at Cloudflare…it means you spend a lot of money on your website. It also means you can enable a “Cache Everything with a Bypass on Cookie” setting that makes things run smoother, but any updates won’t show up. Fortunately, many WordPress caching plugins, such as WP Fastest Cache and the Cloudflare Plugin, connect to your Cloudflare account so it will clear the Cloudflare cache when you add or update a Page or Post. Great…if you have a Business or Enterprise plan.

WP Fastest Cache

What if you’re on a Free or Pro plan?

Luckily, there are Cloudflare Workers that can do this included in the cost of Unlimited Workers ($5/month includes 10 million requests) that will stop those pages from being cached. Add this script to your zone (domain) and set a route, such as example.com/*:

addEventListener('fetch', event => {
  event.respondWith(noCacheOnCookie(event.request))
})

async function noCacheOnCookie(request) {
  // Determine which group this request is in.
  const cookie = request.headers.get('Cookie')
  const cacheSeconds = 604800
  if (cookie 
    && (
      cookie.includes(`wordpress_logged`)
      || cookie.includes(`comment_`)
      || cookie.includes(`wordpress_sec`)
    )) {
    const bustedRequest = new Request(request, { cf: { cacheTtl: -1 } })
    const response = await fetch(bustedRequest)

    const newHeaders = new Headers(response.headers)
    newHeaders.append('wp-cache-busted', `true`)
    return new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers: newHeaders
    })
  } else {
    // Edge Cache for 7 days
    return fetch(new Request(request, { cf: { cacheTtl: cacheSeconds } }))
  }
}
Workers Routes
  1. Main route to cache everything for seven days, as set in the Worker.
  2. This site uses CSP to limit what can load in a browser, but wp-admin needs looser limits.
  3. Add a wp-login route with no Worker so it doesn’t get caught in the cache.

Now all visitor requests to your site will put that response into the Cloudflare cache for seven days. If a site admin updates a page or post, the plugin will clear that Cloudflare cache. If a logged in user visits that page, their WordPress cookie will send a Bypass Cache signal to Cloudflare and pull content directly from the website.

No Page Rules required! A Cache Everything page rule will override the Worker. I only have one Page Rule, and that is to quickly redirect requests to the ‘www’ subdomain to the main site.

In Cloudflare’s Caching section, make sure Caching Level is set to Standard, and Browser Cache Expiration is set to “Respect Existing Headers.”

That should do it!

Visitors will now get a Cache HIT when they visit your page, and site admins or logged in users won’t spoil the cache with their custom views. Those logged in users will have a slightly slower page load time, but all your first time visitors and guests will get a speedy site. Let us know if it’s not working as expected for you.