new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Cloudflare Workers · all subjects

caching/configuration

44 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Enable Workers Caching in wrangler.json

Add a `cache` block to the Wrangler configuration file with `enabled: true` to enable caching. This requires Wrangler 4.69.0 or above. Setting `cache.enabled` to `true` causes Cloudflare to check the cache before invoking the Worker on every HTTP request. The `cache` block accepts two fields: `enabled` (required) and `cross_version_cache` (optional). Any other fields are reserved for future use.

Disable Workers Caching in wrangler.json

Set `cache.enabled` to `false` in the Wrangler configuration to turn caching off, or remove the `cache` block entirely and redeploy. Disabling caching does not purge previously cached responses; it only stops Cloudflare from consulting or populating the cache on subsequent requests. If caching is re-enabled later, any entries still within their TTL become usable again.

Per-entrypoint caching control

A Worker can expose multiple entrypoints (default export and named `WorkerEntrypoint` classes). Use the `exports` map in wrangler.json to override `cache.enabled` per entrypoint. Each entry takes the form `{ "type": "worker", "cache": { "enabled": <boolean> } }`. Per-entrypoint settings override the top-level `cache.enabled` for that entrypoint; unlisted entrypoints inherit the top-level value. This requires Wrangler 4.107.0 or above.

When to disable caching on gateway entrypoints

Disable caching on gateway or router entrypoints that authenticate, normalize, or dispatch requests instead of returning user-facing content. Disabling caching skips cache lookups and goes straight to the gateway logic for lowest latency. Do not enable caching on a gateway and return `Cache-Control: no-store` on all responses, as this still incurs the tiered-cache round trip without benefit.

Cache configuration is part of Worker version

The `cache` configuration is captured with each Worker version uploaded via `wrangler deploy` or `wrangler versions upload`. Rolling back to a previous version also rolls back its attached `cache` setting. During gradual deployments from a version with caching disabled to one with caching enabled, old-version traffic runs uncached and new-version traffic consults and populates the cache. By default, Worker version is part of the cache key, so versions populate independent cache entries.

Cross-version caching behavior

By default, Worker version is part of the cache key and each version has isolated cache. Set `cross_version_cache: true` in the `cache` block to share cached responses across versions. A response written by one version can be served by a later version as long as its TTL has not expired. This maximizes cache hit rate but means cache is not invalidated on deployment; you must purge the cache or tag responses by version for immediate effect. Requires Wrangler 4.107.0 or above.

Environment-specific caching configuration

The `cache` block can be set at the top level and overridden per environment. A typical pattern is to disable caching at top level and enable it only in the production environment, keeping staging uncached for easier debugging.

Caching applies to all fetch invocations

When enabled, Workers Caching applies to every `fetch()` invocation: eyeball requests, service binding `fetch()` calls, and loopback `fetch()` calls between entrypoints via `ctx.exports`, unless caching is disabled per-entrypoint. Custom RPC methods bypass the cache.

Enable Workers Cache in wrangler.toml

Enable Workers Cache by setting cache.enabled to true in the Wrangler configuration file. Example configuration: {"name": "my-worker", "main": "src/index.ts", "compatibility_date": "$today", "cache": {"enabled": true}}

Workers Cache overview and purpose

Workers Cache lets Cloudflare return cached HTTP responses from your Worker without executing your Worker code. When an incoming request matches a cached response, Cloudflare serves the response directly from its edge cache, reducing latency and Workers CPU usage. Caching works for any fetch() invocation of the Worker including eyeball requests, requests through service bindings, and loopback fetch() calls between entrypoints via ctx.exports.

Workers Cache is Worker-owned and private

Workers Cache is owned, operated, and private to your Worker. A Worker is a zoneless entity and can be bound to any number of zones, run on workers.dev, or be invoked through service bindings. The cache follows the Worker, not a zone. Zone-level cache configuration like Cache Rules, Cache Response Rules, Page Rules, cache level settings, and default cached-file-extensions have no effect on a Worker's cache. Your Worker is in full control through Cache-Control headers.

What gets cached in Workers Cache

HTTP invocations of the Worker's fetch handler are eligible for caching, including eyeball requests, service binding fetch() calls, and loopback fetch() calls via ctx.exports. Only GET and HEAD requests are cached; other methods always invoke your Worker. GET and HEAD for the same URL share a single cache entry. Only fetch() invocations go through the cache. Custom RPC methods on a WorkerEntrypoint bypass the cache entirely. WebSocket upgrade requests (GET with Upgrade: websocket) bypass the cache. Scheduled requests, queue consumers, Workflows, Tail Workers, Durable Object invocations, and Email Workers always run without cache involvement.

Tiered cache architecture for Workers

Workers Caching is tiered by default. Cloudflare operates two layers of cache for your Worker: a lower tier in the data center closest to the eyeball (every data center that receives traffic has its own lower-tier cache), and an upper tier in a smaller set of data centers that every lower tier consults on a miss. The upper tier aggregates cache fills across the whole network. A request is served from the lower tier if it hits there. On a miss, the lower tier asks the upper tier. If the upper tier also misses, your Worker runs and the response is stored in both tiers. The tiering runs regardless of whether your Worker uses Smart Placement.

Request collapsing in Workers Cache

When many requests for the same cache key arrive simultaneously at a Cloudflare data center and the response is not yet cached, Cloudflare runs your Worker once and serves the resulting response to every waiting request. This request collapsing mechanism uses a per-cache-key cache lock per data center. Collapsing is per cache key, per data center; requests that produce different cache keys do not collapse with each other. Streaming responses are collapsed too, with waiting requests joined to the in-flight response stream. Collapsing does not apply to uncacheable responses (BYPASS, DYNAMIC); each request gets its own invocation for uncacheable responses.

Vary header for content negotiation in Workers Cache

Workers Caching honors the Vary response header as defined in RFC 9110 and RFC 9111. When your Worker returns a Vary header, Cloudflare stores a separate cached variant per distinct combination of the listed request header values, and only returns a cached variant when the incoming request's headers match. This lets a single URL cache multiple representations (different encodings, content types, or languages) without manual coordination. Vary: * disables caching for the response. Variants share a single cache entry for purge purposes; purging a tag or path prefix that matches any variant invalidates all variants. Responses rewritten by image-transformation features (Polish, Image Resizing) ignore Vary.

Caching between Workers via service bindings

When one Worker calls another over a service binding, the callee's cache is consulted. If the callee has caching enabled and has a matching cached response, the caller receives it without invoking the callee. The cache key for service binding calls includes the caller's ctx.props, so different callers with different authorization context are cached separately. The calling Worker can tailor the callee's caching by setting cf.cacheKey to override the cache key or cf.cacheControl to supply a Cache-Control directive for same-account calls.

Cache Durable Object responses via wrapper entrypoint

Durable Objects are never cached directly by Workers Caching. To cache a Durable Object's HTTP responses, wrap the Durable Object behind a named Worker entrypoint that forwards the request and sets Cache-Control on the response. Because Workers Caching sits in front of the entrypoint, subsequent requests are served from cache without re-entering the Durable Object. Use per-entrypoint caching configuration to disable caching on the default gateway entrypoint and enable it on the cached entrypoint.

Smart Placement and Workers Cache interaction

Smart Placement moves where your Worker runs but does not move the cache. Workers Caching always has a lower tier near the eyeball and an upper tier aggregating the network. The cache is always consulted before Smart Placement is considered. On lower-tier hit, the response is returned from the data center nearest the eyeball without running your Worker. On lower-tier miss and upper-tier hit, the response is returned from the upper tier without running your Worker. On both tiers missing, Smart Placement routes execution to the placement target and the response is stored in both cache tiers. The upper tier and Smart Placement target are independent locations chosen for different purposes.

When caching helps and when it does not

Caching is a good fit for Workers that perform CPU-intensive work whose result can be reused across requests (content generation, template rendering, data transformation), fetch data from a slow origin or third-party API and want to absorb that latency for subsequent requests, or power a server-rendered or statically generated site where many requests produce identical responses. Caching is not useful for per-user responses that change on every request, non-idempotent operations (POST, PUT, DELETE), or responses that must be computed fresh every time.

Preview URLs and Workers for Platforms caching support

Preview URLs are supported by Workers Cache. Each preview caches independently of your production deployment, so testing a cache-affecting change in a preview never touches production's cached responses. Workers for Platforms is supported; each user Worker has its own cache, isolated from the dispatcher and from other user Workers in the namespace.

How Workers Cache works - flow

With caching enabled, Cloudflare checks the cache before running your Worker. On a cache hit, the cached response is returned directly. On a cache miss, your Worker runs, and if the response is cacheable per its Cache-Control header, Cloudflare stores it for the next request.

cache.put requires cloned response

When using cache.put() to store a response, you must pass a cloned response: cache.put(cacheKey, response.clone()). This is necessary because the response body can only be read once.

caches.default is the default cache instance

Access Cloudflare's default cache using caches.default. This is the cache instance used by the Cache API.

Cache API basic pattern with cache.match and cache.put

To use the Cache API in a Worker, check if a response is cached using cache.match(cacheKey), and if not found, fetch the response from origin and store it using cache.put(cacheKey, response.clone()). The cacheKey is constructed as a new Request object from the cache URL. Use ctx.waitUntil() to ensure the cache write completes without blocking the response.

Cache API example with JavaScript

export default { async fetch(request, env, ctx) { const cacheUrl = new URL(request.url); const cacheKey = new Request(cacheUrl.toString(), request); const cache = caches.default; let response = await cache.match(cacheKey); if (!response) { console.log( `Response for request url: ${request.url} not present in cache. Fetching and caching request.`, ); response = await fetch(request); response = new Response(response.body, response); response.headers.append("Cache-Control", "s-maxage=10"); ctx.waitUntil(cache.put(cacheKey, response.clone())); } else { console.log(`Cache hit for: ${request.url}.`); } return response; }, };

Cache POST requests using body hash as cache key

POST requests cannot be cached directly by the Cache API because it only caches GET requests. To cache POST requests, hash the request body using SHA-256, create a modified cache key by converting the request to GET and prepending the body hash to the pathname, then check the cache with this modified key. If the response is not cached, fetch the original POST request and store the response using ctx.waitUntil(cache.put()) to avoid blocking the response.

Cache POST request example in JavaScript

export default { async fetch(request, env, ctx) { async function sha256(message) { const msgBuffer = await new TextEncoder().encode(message); const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer); return [...new Uint8Array(hashBuffer)] .map((b) => b.toString(16).padStart(2, "0")) .join(""); } try { if (request.method.toUpperCase() === "POST") { const body = await request.clone().text(); const hash = await sha256(body); const cacheUrl = new URL(request.url); cacheUrl.pathname = "/posts" + cacheUrl.pathname + hash; const cacheKey = new Request(cacheUrl.toString(), { headers: request.headers, method: "GET", }); const cache = caches.default; let response = await cache.match(cacheKey); if (!response) { response = await fetch(request); ctx.waitUntil(cache.put(cacheKey, response.clone())); } return response; } return fetch(request); } catch (e) { return new Response("Error thrown " + e.message); } }, };

Cache POST request example in TypeScript

interface Env {} export default { async fetch(request, env, ctx): Promise<Response> { async function sha256(message) { const msgBuffer = await new TextEncoder().encode(message); const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer); return [...new Uint8Array(hashBuffer)] .map((b) => b.toString(16).padStart(2, "0")) .join(""); } try { if (request.method.toUpperCase() === "POST") { const body = await request.clone().text(); const hash = await sha256(body); const cacheUrl = new URL(request.url); cacheUrl.pathname = "/posts" + cacheUrl.pathname + hash; const cacheKey = new Request(cacheUrl.toString(), { headers: request.headers, method: "GET", }); const cache = caches.default; let response = await cache.match(cacheKey); if (!response) { response = await fetch(request); ctx.waitUntil(cache.put(cacheKey, response.clone())); } return response; } return fetch(request); } catch (e) { return new Response("Error thrown " + e.message); } }, } satisfies ExportedHandler<Env>;

Cache POST request example in Python

import hashlib from workers import WorkerEntrypoint from pyodide.ffi import create_proxy from js import fetch, URL, Headers, Request, caches class Default(WorkerEntrypoint): async def fetch(self, request, _, ctx): if 'POST' in request.method: body = await request.clone().text() body_hash = hashlib.sha256(body.encode('UTF-8')).hexdigest() cache_url = URL.new(request.url) cache_url.pathname = "/posts" + cache_url.pathname + body_hash headers = Headers.new(dict(request.headers).items()) cache_key = Request.new(cache_url.toString(), method='GET', headers=headers) cache = caches.default response = await cache.match(cache_key) if response is None: response = await fetch(request) ctx.waitUntil(create_proxy(cache.put(cache_key, response.clone()))) return response return fetch(request)

Cache POST request example in Hono

import { Hono } from "hono"; import { sha256 } from "hono/utils/crypto"; const app = new Hono(); app.post("*", async (c) => { try { const body = await c.req.raw.clone().text(); const hash = await sha256(body); const cacheUrl = new URL(c.req.url); cacheUrl.pathname = "/posts" + cacheUrl.pathname + hash; const cacheKey = new Request(cacheUrl.toString(), { headers: c.req.raw.headers, method: "GET", }); const cache = caches.default; let response = await cache.match(cacheKey); if (!response) { response = await fetch(c.req.raw); c.executionCtx.waitUntil(cache.put(cacheKey, response.clone())); } return response; } catch (e) { return c.text("Error thrown " + e.message, 500); } }); app.all("*", (c) => { return fetch(c.req.raw); }); export default app;

cacheEverything option in fetch cf object

The cacheEverything option forces Cloudflare to cache an asset regardless of its content type or origin headers. Setting cf.cacheEverything: true overrides the default cacheability of an asset. When used, Cloudflare will still rely on headers set by the origin for TTL unless cacheTtl or cacheTtlByStatus is also specified.

cacheTtl option in fetch cf object

The cacheTtl option sets the time-to-live for cached responses in seconds. In fetch requests, use cf.cacheTtl to specify how long Cloudflare should cache a response before revalidating with the origin. For example, cacheTtl: 5 caches for 5 seconds.

cf.vary property for Vary header handling

Use cf.vary in fetch options when an origin returns a Vary header and you want a Worker subrequest to cache expected variants. The cf.vary property accepts an object with a 'default' action and a 'headers' object containing header-specific normalization rules. For example, you can set action: 'normalize' with media_types or languages arrays to normalize those header values for caching.

cacheTtlByStatus option in fetch cf object

The cacheTtlByStatus option chooses a TTL based on the response's HTTP status code. It accepts an object with status code ranges or specific codes as keys and TTL values in seconds. For example, cf.cacheTtlByStatus: { '200-299': 86400, 404: 1, '500-599': 0 } caches successful responses for 86400 seconds, 404s for 1 second, and does not cache 5xx errors. This does not automatically set cacheEverything: true and overrides cache directives sent by the origin.

Response object reconstruction to set mutable headers

To set cache control headers on a fetched response, you must reconstruct the Response object. Use new Response(response.body, response) to create a new Response that allows header modification. Then use response.headers.set() to add or modify headers on the new response object.

cache fetch option modes: no-store and no-cache

The cache option in fetch controls HTTP cache behavior. Currently Workers supports two modes: 'no-store' bypasses the cache on the way to the origin and makes the request not cacheable; 'no-cache' forces the cache to revalidate the currently cached response with the origin. Use fetch(request, { cache: 'no-store' }) or fetch(request, { cache: 'no-cache' }).

Example: Cache using fetch with custom cache key and TTL

export default { async fetch(request) { const url = new URL(request.url); const someCustomKey = `https://${url.hostname}${url.pathname}`; let response = await fetch(request, { cf: { cacheTtl: 5, cacheEverything: true, cacheKey: someCustomKey, }, }); response = new Response(response.body, response); response.headers.set("Cache-Control", "max-age=1500"); return response; }, };

Example: cf.vary with Accept and Accept-Language headers

export default { async fetch(request): Promise<Response> { return fetch(request, { cf: { vary: { default: { action: "bypass" }, headers: { accept: { action: "normalize", media_types: ["text/html", "application/json"], }, "accept-language": { action: "normalize", languages: ["en", "fr", "de"], }, }, }, }, }); }, } satisfies ExportedHandler;

Example: cacheTtlByStatus for different response codes

fetch(request, { cf: { cacheTtlByStatus: { "200-299": 86400, 404: 1, "500-599": 0 } }, });

Workers Caching billing

When Workers Caching is enabled, requests served from the Worker's cache are billed at the same per-request rate as requests that invoke the Worker. This includes requests to static assets and worker-to-worker invocations. CPU time is only billed when the Worker runs (on a cache miss or bypass).

Cache Response Rules versioning use cases

Cache Response Rules versioning enables you to test response-phase rules in a staging environment before promoting to production, catch unintended caching side effects early, and run different response-phase cache settings per environment. For example, you can strip Set-Cookie headers in staging to validate cacheability without affecting production traffic.

Cache Response Rules configuration access

Cache Response Rules can be configured in the Cloudflare dashboard under Caching > Cache Rules, or via the Rulesets API.

Cache Response Rules support Zone Versioning

Cache Response Rules are now fully integrated with Version Management, allowing you to version response-phase cache settings and promote them through environments before deploying to production. Previously, Cache Response Rules were excluded from zone versioning and applied globally across all environments with no ability to test changes in staging first.

Cache Response Rules response-phase operations

Cache Response Rules response phase allows you to modify Cache-Control directives, manage cache tags, and strip headers.

Give your agent this brain