Cloudflare Workers: Edge caching anonymous requests

Granted my site is pretty small, but speed drastically improved after setting up Cloudflare edge caching:

my site is faster than yours

In this Cloudflare Blog Post titled ‘Caching Anonymous Page Views’ it is outlined that you can cache anonymous page views on their edge servers and bypass any edge caches with certain cookies:

Cloudflare is determined to help website administrators boost the performance of their websites. From today, Cloudflare users on our Business plan will gain a previously Enterprise-only Page Rule option, “Bypass Cache on Cookie”. When used in conjunction with a “Cache Everything” Page Rule, this setting allows for websites to cache the HTML of anonymous page visits without affecting dynamic content.

By caching anonymous page views, Cloudflare is able to help ensure that your origin webserver doesn’t waste time constantly regenerating pages which change rarely. This ultimately allows us to reduce load on your server and reduce load times when users reach your site.

However with the recent introduction of Cloudflare Workers it is possible to run javascript workers on edge servers with 5ms of execution time to evaluate/modify a http request and response. It’s also possible to control how other features affect the request such as ‘ScrapeShield’, ‘Polish’, ‘Minify’ etc. Here we are going to modify the Edge Cache TTL to instruct Cloudflare not to cache the response at all upon matching the following cookies: wordpress_logged*, comment_*, wordpress_sec*.

Here’s the code: 

// Stop CF edge from caching your site when specific wordpress cookies are present
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 } }))
  }
}

https://gist.github.com/mmaton/8e1a6b0cf4033dc96a17bb40e1d19b93/raw/82a5c1a5238d0264b1f7e3b6930d68f8c4f87698/Wordpress%2520CF%2520Edge%2520Cache%2520Busting

Positive TTL values indicate in seconds how long Cloudflare should cache the asset for: a TTL of 0 will cause assets to get cached, but expire immediately (revalidate from origin every time), -1, or any negative value will instruct Cloudflare not to cache at all.

Testing using httpstat without triggering cookies (anonymous request)

⇒  httpstat -I https://www.mmaton.com/
Connected to 104.28.21.21:443 from 5.196.161.0:39282

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8Cache-Control: private, max-age=2678400
CF-Cache-Status: HIT
CF-RAY: 448bee17ac8172b3-AMS
Server: cloudflare

Body stored in: /tmp/tmpbznFVd

  DNS Lookup   TCP Connection   Server Processing   Content Transfer
[    28ms    |      207ms     |       74ms        |        0ms       ]
             |                |                   |                  |
    namelookup:28ms           |                   |                  |
                        connect:235ms             |                  |
                                      starttransfer:326ms            |
                                                                 total:326ms 

And now with cookies which will bypass the cache (logged in user/commenter):

⇒  httpstat -I https://www.mmaton.com/ --cookie "wordpress_logged_in=hello"
Connected to 104.28.21.21:443 from 5.196.161.0:39286

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
wp-cache-busted: true
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Server: cloudflare

Body stored in: /tmp/tmpcnSUnI

  DNS Lookup   TCP Connection   Server Processing   Content Transfer
[    125ms   |      205ms     |       75ms        |        0ms       ]
             |                |                   |                  |
    namelookup:125ms          |                   |                  |
                        connect:330ms             |                  |
                                      starttransfer:418ms            |
                                                                 total:418ms

I’ve now shaved off ~100ms from my response times when the site is edge-cached! This will also hopefully require less server resources too as more hits will be cached at edge.

Great, now I don’t need to pay $200 a month for a CF business plan, just $5 for the worker (and any requests exceeding 10,000,000)