The answer. For years the way to cache inside a Worker was the Cache API: caches.default.match(request), and on a miss fetch the origin and cache.put(request, response.clone()). It still works and it still has the same sharp edges: only GET requests, a cache scoped to the data center the Worker ran in, no request collapsing (a burst of misses invokes the origin once per request), and whatever Cache-Control says is what you get, which is how a transient error got pinned for 24 hours in our ingest Worker. Workers Caching is the newer, read-through model: enable cache on an exported entrypoint and Cloudflare caches its responses by URL according to Cache-Control, collapses concurrent misses, participates in tiered caching, and invalidates with ctx.cache.purge({ tags }) from code. Put the gateway logic (auth, header stripping) in an uncached default entrypoint and the cacheable work in a second, cached one.
The pattern.
[cache]
enabled = true
[exports.default]
type = "worker"
[exports.default.cache]
enabled = false # gateway: runs every time, strips Authorization
[exports.CachedApi]
type = "worker"
[exports.CachedApi.cache]
enabled = true # read-through cache in front of this entrypoint
export class CachedApi extends WorkerEntrypoint<Env> {
async fetch(request: Request) {
const data = await loadExpensive(request, this.env);
return Response.json(data, { headers: { "cache-control": "public, max-age=60, stale-while-revalidate=600", "cache-tag": "products,product:42" } });
}
}
export default { async fetch(request, env, ctx) {
if (!(await authenticate(request, env))) return new Response("unauthorized", { status: 401 });
const fwd = new Request(request); fwd.headers.delete("authorization"); // Authorization forces BYPASS
return ctx.exports.CachedApi.fetch(fwd);
} };
Watch out.
- Neither layer caches a response with
Set-Cookie, and the Cache API rejectsVary: *and 206s outright. cache.delete()purges only the local data center; global invalidation needsCache-Tagand a purge-by-tag, orctx.cache.purge.- Cache errors deliberately: give 5xx responses
Cache-Control: no-storebefore anyput().
Related: cache-api-poisoned-by-transient-error · kv-cache-upstream-json-with-ttl-and-stale-fallback · r2-serve-objects-with-their-own-headers-etag-range