INTEGRITY Cloudflare Docs

Debugging

If caching is not behaving the way you expect, the Cf-Cache-Status response header is the first place to look. Every response carries it, and its value tells you exactly what happened for that request.

Inspect Cf-Cache-Status

Send two requests to the same URL and compare the headers:

curl -I https://my-worker.example.workers.dev/api/users/42
curl -I https://my-worker.example.workers.dev/api/users/42

Match the status value against the scenarios below.

My Worker runs on every request

Cf-Cache-Status is not present. Check that your wrangler version is 4.69.0 or above, and that wrangler.toml or wrangler.jsonc has cache.enabled = true for that worker.

Cf-Cache-Status is MISS on every request, or DYNAMIC, or BYPASS. Caching is not storing anything, or a bypass rule is firing.

Check your Cache-Control header. The response must carry directives that make it cacheable:

A response with Cache-Control: private or no-store is not stored, and Cf-Cache-Status is BYPASS.

A response with Cache-Control: no-cache is stored, but Cloudflare treats every subsequent request as stale and consults your Worker before serving. The exact Cf-Cache-Status depends on whether stale-while-revalidate is also set:

If you wanted long-lived cache hits, use max-age instead. Refer to no-cache is not a bypass.

If the response carries no Cache-Control header at all, behavior depends on the status code: Workers Caching applies RFC 9111 heuristic freshness and caches default-cacheable status codes for a heuristic TTL — for example, 200 is cached for 2 hours and 404 for 3 minutes. For the full table of default TTLs, refer to Responses with no Cache-Control header are still cached in the configuration reference. If you do not want any of these defaults to apply, set Cache-Control explicitly on the response.

Check the request method. Only GET and HEAD requests are cached. Everything else is BYPASS. GET and HEAD requests for the same URL share the same cache entry — refer to Cache keys for how Cloudflare handles populating the cache from either method.

Check for automatic bypass conditions. Cloudflare bypasses the cache when:

If your Worker unconditionally sets Set-Cookie (for example, a session cookie on every response), the response is never cached. Either remove the cookie from cacheable responses, or separate cookie-setting and cacheable responses into different routes.

Check the status code. Workers Caching follows RFC 9111. Responses with status codes that are not cacheable by default (for example, 401, 403, 500) are not stored unless you explicitly mark them with cacheable directives.

A few status codes are never cached, even with explicit Cache-Control:

My Worker runs even after the first request

Cf-Cache-Status is MISS on the first request but still MISS on subsequent requests.

The cache is likely partitioned. The cache key includes the request path, the target entrypoint, and the invocation's ctx.props. Two requests that look the same to you may produce different cache keys if any of these differ.

Common causes:

Cloudflare does not currently expose the cache key composition, so you cannot see the computed key directly. Instead, walk through the components listed in Cache keys and verify each one is the same for both requests.

My cache hit rate dropped after a deployment

This is expected with the default configuration. By default, the Worker version is part of the cache key, so each new version starts from a cold cache and cannot reuse the previous version's cached responses. The first requests after a deploy are misses while the new version's cache fills, then the hit rate recovers.

If you deploy frequently and your responses rarely change between deployments, enable cache.cross_version_cache to share cached responses across versions and avoid resetting the cache on every deploy. The trade-off is that cache-affecting changes no longer apply immediately — see below.

My cache still serves old content after a deployment

By default a deployment takes effect immediately, because the Worker version is part of the cache key and the new version starts from a cold cache. If you are still seeing responses from a previous version, you have cache.cross_version_cache enabled, which shares cached entries across versions. To force a deployment to take effect while keeping cross_version_cache on:

My cache never updates after content changes

If your origin data changed but requests still return stale content:

Two callers receive each other's cached responses

This should not happen if you use ctx.props for per-caller authorization context. If it does, one of the following is true:

Cf-Cache-Status: UPDATING appears constantly

UPDATING means the response was served from cache while stale and your Worker is running in the background to refresh it. This is expected behavior when using stale-while-revalidate.

If you see UPDATING more often than you expect:

Cf-Cache-Status: UPDATING never appears

UPDATING is emitted only when all of the following are true:

If any of those is false, requests for stale entries fall through to inline revalidation instead, producing EXPIRED (Worker returned a fresh body) or REVALIDATED (Worker returned 304 Not Modified).

Common reasons UPDATING does not appear:

Cf-Cache-Status: STALE appears unexpectedly

STALE means Cloudflare served a previously cached response because your Worker errored on the request that would have refreshed it — for example, the Worker threw, timed out, or returned a 5xx response. This is stale-if-error behavior. Refer to Serve stale on error with stale-if-error.

If you see STALE and did not expect it:

To distinguish a STALE from a normal HIT in client-side observability, log Cf-Cache-Status alongside the response — STALE is the only signal that the Worker is failing and clients are not seeing it.

My response is larger than the size limit

If a response is too large to cache, Cloudflare does not store it. You will see Cf-Cache-Status: MISS on every request even though the response otherwise looks cacheable.

For per-plan response size limits, refer to Cacheable size limits. Note that at launch all Workers Caching responses are subject to the Free plan size limit — refer to Response size for details.

I need more visibility

At launch, the primary debugging surfaces are the Cf-Cache-Status response header and per-invocation cache-hit information in the Workers observability dashboard.