Skip to content

Investigate uncached responses

Last updated View as MarkdownAgent setup

If a URL you expected to be cached is served from the origin every time, the cf-cache-status response header identifies which cache decision Cloudflare made. Fetch the URL, inspect the header, and follow the section that matches the value you see:

For any other status, refer to Cache responses for the full list.

Before you start

A recent Purge Everything, purge by URL, purge by prefix, purge by tag, or purge by hostname clears the cache. The next request in each data center repopulates cache and returns MISS before subsequent requests return HIT. If a purge ran recently, wait for the cache to repopulate before continuing.

DYNAMIC — request not eligible for cache

Cloudflare made the "do not cache" decision at request time, before looking in the cache. Common causes:

  • The file extension is not in the default cached file extensions list — for example, .html or a JSON API response — and no rule enables caching for it. Add a Cache Rule with Eligible for cache set to Yes.
  • A rule instructs Cloudflare to bypass cache. Check whether a Cache Rule with the Bypass cache setting, or a legacy Cache Level: Bypass Configuration Rule or Page Rule, matches the URL. Use Rule Trace to confirm which rules apply.
  • The request method is not GET or HEAD. Cloudflare only caches these two methods.
  • Development Mode is enabled on the zone. Development Mode suspends cache for three hours and returns DYNAMIC for every response.

Once the request is eligible, subsequent responses reflect the response-time decision (HIT, MISS, BYPASS, and so on).

BYPASS — origin response is not cacheable

The request was eligible for cache, but the origin response or configuration prevented Cloudflare from storing it. Common causes:

  • The response exceeds the cacheable size limit for your plan. Split the object into smaller assets, or move to a plan with a higher limit. R2 is an origin storage alternative — it does not raise the CDN cacheable size limit.

  • The origin returned Cache-Control: no-store or bare private. These directives block caching in either Origin Cache Control mode by default. Two exceptions: Cache-Control: private="<header>" with field names remains cacheable — Cloudflare drops only the named headers. And a Cache Rule with an Edge TTL that ignores origin cache-control (Edge TTL → Ignore cache-control header and use this TTL or Status code TTL) overrides both directives, so a response with no-store plus that Edge TTL setting is cached.

  • The origin returned Cache-Control: no-cache, max-age=0, or s-maxage=0, and Origin Cache Control is disabled (the default on Enterprise plans). With Origin Cache Control enabled (the default on Free, Pro, and Business plans), these directives cause Cloudflare to cache and revalidate the response instead, producing REVALIDATED or EXPIRED. Refer to Understand no-store and no-cache directives and the Conditions table.

  • The origin returned a Set-Cookie header. By default, Cloudflare does not cache responses that include Set-Cookie. To cache the response, use one of the following:

    • Set an explicit Edge TTL on a Cache Rule using Edge TTL → Ignore cache-control header and use this TTL or Status code TTL. Cloudflare ignores the origin's directives, strips Set-Cookie, and caches the response.
    • Have the origin return Cache-Control: private="Set-Cookie" or no-cache="Set-Cookie". Cloudflare drops the named header and caches the rest.
    • Strip Set-Cookie before the cache decision, using a Response Header Modification Transform Rule.
    • On Enterprise plans with Origin Cache Control disabled, Cloudflare strips Set-Cookie and caches the response under the default cache level. A Cache Level: Cache Everything Page Rule or a Cache Rule with Eligible for cache set to Yes — either without an explicit Edge TTL — overrides this and returns BYPASS.

    Refer to Interaction of Set-Cookie response header with Cache for the full matrix.

  • The origin returned Vary: *. This value always bypasses cache, regardless of other configuration.

  • The request included an Authorization header and Origin Cache Control is enabled (the default on Free, Pro, and Business plans). In that mode, the response is cacheable only if Cache-Control also includes public, s-maxage, or must-revalidate. On Enterprise plans with Origin Cache Control disabled, Authorization does not by itself prevent caching.

Refer to BYPASS for the reference definition of this status.

Repeated MISS — cacheable but not in cache

MISS on the first request in each data center is expected — that request populates the cache. If the same URL keeps returning MISS across consecutive requests, one of the following is happening.

Cache key variance

Cloudflare builds the cache key from the origin scheme, host, path, and query string by default. Cookies, headers, and device type can also contribute when configured. The scheme in the cache key is the scheme Cloudflare uses to reach the origin, not the scheme the client used — a zone with a single origin scheme serves HTTP and HTTPS client requests from the same cache entry.

If each real client request produces a different key, the cache never sees a repeat and every request is a MISS. Common patterns:

  • Query parameters that change per request — session IDs, timestamps, or marketing tags such as utm_*. By default, every unique query string is a separate cache entry. Configure Cache Rules or Cache Key Settings to exclude or ignore parameters whose value changes per request. Sorting only canonicalizes parameter order — use it when requests differ only in the order of parameters, not when the values differ.
  • A custom cache key includes a cookie or header with a value that changes per user. The Create custom cache keys section warns that custom keys "may reduce your cache hit rate and result in cache sharding" — this is the same behavior.
  • Cache by device type classifies clients differently than expected, particularly for bots or clients with unusual User-Agent values.
  • Vary in Cache Rules is configured for a header, the origin lists that header in its Vary response header, and the action is normalize or passthrough. Each response variant is stored under a separate key. When the header has high cardinality — for example, an unnormalized Accept-Language value or a per-user header — the practical hit rate drops. The bypass action prevents caching entirely.

Two identical requests produce the same cache key and cannot reveal variance. To diagnose, use Rule Trace to see the applied cache-key configuration and Vary action for the URL, then compare that against the request attributes (query string, cookies, headers, device type) that differ between real client requests.

Eviction and low-traffic assets

Low-traffic assets can be evicted from the cache before the next request arrives. If two consecutive requests to the same URL from the same data center both return MISS, enable Tiered Cache or Cache Reserve to retain long-tail content longer.

If your requests reach different Cloudflare data centers, each produces its own first-request MISS. Compare the data center code — the last three characters of the cf-ray header — to confirm two responses came from the same data center. Different client networks can still reach the same data center, so a network change does not guarantee a different location.

Confirm the response reaches cache

After adjusting configuration, request the URL twice from the same client and verify the expected outcome for your configuration:

  • Fresh, positive Edge TTL: cf-cache-status: HIT and an Age header that increases on subsequent requests. Age may be absent on the first lower-tier request that populates the local cache from a tiered cache fill (CacheTieredFill=true).
  • Origin returns Cache-Control: no-cache with Origin Cache Control enabled: cf-cache-status: REVALIDATED when the origin confirms the cached copy is unchanged, or EXPIRED when the origin returns new content. Both indicate the response is cached. must-revalidate on its own does not force revalidation on every request — it only prevents serving stale content after the freshness TTL expires.

If the response is still MISS or BYPASS after these checks, capture two full responses (request and response headers, including cf-ray values) and open a support case. The cf-ray values are required to trace the request through the Cloudflare network.

Was this helpful?