In Cloudflare Workers, I am seeking clarification on calling "delete" from the Cache API and "not replicated to any other data centers". Or, what's the best way to purge custom cache keys on all data centers from inside of worker?

The Cloudflare Workers Cache API says:

For each individual zone, the Cloudflare Workers runtime exposes a single global cache object: caches.default . Though this cache object persists on all of Cloudflare’s data centers, objects are not replicated to any other data centers. Note this individualized zone cache object differs from Cloudflare’s Global CDN, for details see: Using the Cache.

Okay – awesome, so if I had a worker with two routes (pseudo-example code only):

/cache-me-outside
/delete-cache-me-outside

Hitting /cache-me-ouside does a bunch of things and then runs:

cache.put(sameRequest123, response)

Hitting `/delete-cache-me-outside does:

cache.delete(sameRequest123, options)

If cache.put is not replicated across data centers, does this mean calling cache.delete is also only for the connected data center? That would make sense.

Aka, cache.delete doesn’t purge all caches of the custom cache key but only of the current data center?

Then what’s the best way to purge custom cache keys on all data centers from inside of worker?

Correct.

For that the API would come in handy

https://api.cloudflare.com/#zone-purge-files-by-url

To purge files with custom cache keys, include the headers used to compute the cache key as in the example. To purge files with {geo} or {devicetype} in their cache keys, include the CF-Device-Type or CF-IPCountry headers.

1 Like

Great, thanks for quick reply. That’s where I figured this would land and this makes sense!

Have you verified that this works? It would be great if it did.

I just tried calling that Zones > Purge Files By URL api with the same url as the url used to store something in caches.default.put (a basic example with no complex cache key headers) - and it does not seem to invalidate the caches.default cache.

Would love some confirmation from cloudflare whether or not the purge api affects the caches.default cache.

I couldnt imagine why it shouldnt as that call purges the cache. As the response says, you might have to wait a minute or so until it reached all datacentres.

Yea it would certainly be nice - but it doesn’t work, even after waiting several minutes. Have you tested it out over on your side?

This might be another case of where the worker cache api (caches.default) behaves differently from the normal cloudflare cache. At least we can caches.default.delete… : )

You might want to reach out to support if you can reproduce it.

I did this and it worked. I will triple verify tomorrow…

Pretty sure if your cache key URL is the same as the zones then it would work.

Or, if you are baller on enterprise and can purge by tag or origin or whatever extras.

Don’t like that I have to hit the API to do this but it’s logical

1 Like

Finally following up here, I can confirm 100% this works @john.spurlock . You can purge by individual URL to delete Cache API request. At least from the dashboard.

Since the API is cache.put(request, response), your cache key can literally be an object.

You need to be diligent with this request object as your key. I got some weird behavior by messing this up originally where I’m guessing you are landing. Or, your Browser TTL is way too high serving simple 200s.

It’s easier to just set the URL since it’s still valid and probably works most cases.

Pseduo-code quick example:

// Get Current URL
const CURRENT_URL_PARTS = new URL(request.url)
const CACHE_KEY = CURRENT_URL_PARTS.origin+CURRENT_URL_PARTS.pathname // full URL, no query param but you can do whatever you want...

// Check if incoming request has a cache object...
let cachedResponse = await cache.match(CACHE_KEY)

if (cachedResponse) {

       // Maybe customize this a bit...
       let cachedHeaders = new Headers(cachedResponse.headers)
       cachedHeaders.set('I am a Totally', 'Custom Thing')

       // Set your Browser TTL going to the client
       cachedHeaders.set('Cache-Control', 'max-age=60') // 1 minute

       return new Response(cachedResponse, {
             status: 200,
             cachedHeaders
       })

} else {

        // do what you need to do here...
        let response = getComplicatedResponseCode()

        // Maybe customize this a bit for Cloudflare's data centers...
        let headers = new Headers(response.headers)
        headers.set('secret-message', 'cloudflares-data-centers-only-see-me')

        // Set Edge TTL (this is what gets cleared)
        headers.set('Cache-Control', 'max-age=300') // 5 minutes

        // Put in cache...
        event.waitUntil(cache.put(CACHE_KEY, response.clone()))

        // Adjust response now to send to client...
        headers.set('secret-message', 'client-only-sees-me')

        // Now change Cache-Control for client (aka, browser TTL)...
        headers.set('Cache-Control', 'max-age=60') // 1 minutes

        // return it...
        return new Response(response, {
             status: 200,
             headers
        })

}

To understand this code, imagine you hit refresh every second on a page for 5 minutes.

  • The request at 1s is new and the response is not served from Cloudflare cache (in a sense).
  • The request at 60s is new again and the response served is from Cloudflare cache.
  • This repeats until at 5 minutes…
  • At 300 seconds (5 mins), the entire thing starts all over. New request, request not served from cache, new cache created

Hope that helps!