xSpeed Cache is officially live! Get 50% OFF during Launch Week — Lifetime starts at just $79Lifetime from $79 — 50% OFF, Launch Week! See Plans →

See Plans →
Features
xSpeed Hub Pricing Docs Blog Scan
Appearance
Get Plugin
All articles
CDNCloudflareTutorialCachingplatform:cloudflarecheck:D5

Prove the Edge Is Answering in 2026: One Response Can Report a Cache Hit and DYNAMIC at the Same Time

xSpeed Cache Team 10 min read
xSpeed Cache cover reading "Did the edge really answer? Two headers, two requests, one real URL", with Cloudflare, bunny.net and WordPress logos and three numbered steps: send a GET twice, read the status word, then read Age.

Updated October 2026

Cloudflare’s default cache behavior documentation, fetched 4 October 2026, lists 56 file extensions it caches without being asked, and states HTML is not one of them. Our own probe of 556 Cloudflare-fronted WordPress sites found 330 returning cf-cache-status: DYNAMIC, meaning no cache lookup happened. The check most people run is a single curl -I, and what they read is a single header. One header cannot settle this. A response can report a cache hit and DYNAMIC in the same breath, because the two words describe two different caches, and two managed WordPress hosts do exactly that. Here is how to read the receipt.

Quick Summary: What the Status Word Tells You to Do Next

If you see this on your HTMLDo thisWhy
HIT with an AgeNothingIt came out of edge storage
DYNAMICAdd a cache rule marking the document eligibleNothing looked in the cache
BYPASSFix the origin response, not the ruleIt was eligible and the origin refused
A host HIT beside DYNAMICTreat both as trueYour origin cached it, the edge did not

Nine Cloudflare cache status values, whether each means an edge answered, and whether Cloudflare sets Age

The Document Is the Only Response That Counts

Cloudflare’s default cache behavior documentation states that it “only caches based on file extension and not by MIME type”, and that “the Cloudflare CDN does not cache HTML or JSON by default”. Counted from that page on 4 October 2026, the list holds 56 extensions, JPG, CSS and MP4 among them. A WordPress pretty permalink ends in a slash and carries no extension, so it cannot land on that list by accident.

A report can therefore show a CDN in front while every page still comes from your server. The images are at the edge. The document is not. That gap is what our CDN adoption study measured, and it is the half of the wait behind a page that is fast for you and slow abroad.

Two Hosts, One Response, Two Verdicts

A GET with a browser user agent on 4 October 2026 returned this from kinsta.com/blog/:

cf-cache-status: DYNAMIC
x-kinsta-cache: HIT

And this from wpengine.com/blog/:

cf-cache-status: DYNAMIC
x-cache: HIT: 5
x-cacheable: SHORT

Both are telling the truth. The page is cached at the host’s own layer, and Cloudflare proxied it without a cache lookup. Read only the vendor status and nothing is cached; read only the host header and the edge answers. Neither survives the other, which is why the test is a pair.

The vocabulary differs per vendor, and every value below was read live that day:

Front endHeader to readValue measured 4 October 2026
Cloudflarecf-cache-status, cf-rayHIT, ray suffix IAD
Fastlyx-cache, x-cache-hits, x-served-byMISS, HIT and 0, 4
CloudFrontX-Cache, X-Amz-Cf-PopHit from cloudfront, IAD61-P11
BunnyCDNcdn-cache, cdn-requestcountrycodeHIT, US

Those came from wpbeginner.com, python.org, docs.aws.amazon.com and bunny.net; KeyCDN and Sucuri answer x-cache and x-sucuri-cache the same way. Fastly prints one value per node in the chain, so MISS, HIT is two caches in one string, not a contradiction. None of it is standardised.

DYNAMIC and BYPASS Name Two Different Culprits

These two carry the diagnosis and are read as synonyms. Cloudflare’s documentation is explicit: DYNAMIC “is only returned when Cloudflare determines the asset is not eligible for cache at request time”, while a rule marking HTML eligible plus an origin returning a non-cacheable directive produces “CF-Cache-Status: BYPASS — not DYNAMIC”.

StatusDecided atCause on a WordPress siteWhat to change
DYNAMICRequest timeNo rule marks the document cacheable, a bypass rule matched, or Development Mode is onThe rule, in your dashboard
BYPASSResponse timeA Set-Cookie, Cache-Control: no-store or private, a Vary: *, or an Authorization headerThe response your server sends

The Set-Cookie row bites WordPress hardest: a comment form, a session notice or a cart on a WooCommerce store can attach one to an anonymous page view. The rule is then perfect and the page still uncached.

DYNAMIC decided at request time against BYPASS decided at response time, with the fix each one needs

The Age Header Settles Nothing, in Either Direction

Age looks like the clean answer and is the most misread header here. Both halves of the problem are written down. RFC 9111 section 5.1 says the presence of an Age field “implies that the response was not generated or validated by the origin server for this request”, then adds the sentence nobody quotes: “lack of an Age header field does not imply the origin was contacted”. A missing Age is evidence of nothing.

Cloudflare’s cache responses documentation, updated 4 September 2026, closes the other half. It sets Age on HIT, STALE and UPDATING, and not on MISS, DYNAMIC, BYPASS or NONE/UNKNOWN. But “if the origin sets Age on an uncacheable response, that value is proxied to the client unchanged”. An Age beside DYNAMIC came from your server. Under Tiered Cache it can also be inherited from an upper tier, so it can be older than the local copy.

Read the status word first, and use Age only on a copy already confirmed.

Send a GET, Not a HEAD, and Send It Twice

curl -I sends a HEAD request. Cloudflare’s default behavior page lists “the HTTP request method is anything other than a GET” among the conditions under which it does not cache a resource, and an origin cache can refuse a HEAD too.

Send a GET and throw the body away instead:

curl -s -D - -o /dev/null -A 'Mozilla/5.0' https://example.com/your-page/

Then send it again. A first request can store the page on its way past, which is what MISS means, so only a second shows storage being read. One request can disprove a cache. It can never prove one.

The origin layer refuses a HEAD for its own reasons, and ours says so out loud. Our cache HITs and MISSes guide documents X-XSpeed-Reason: non-get as a gate producing a BYPASS, and names the HEAD request curl -I sends as a case of it. xSpeed Cache is ours, built by WPDeveloper, and that header exists to make the second verdict a read instead of a guess.

The Edge Receipt: Four Questions, One Header Each

Run these on one real post URL, never the home page, which is the URL a misconfigured rule most often gets right.

  1. Is a CDN in front at all? Read server and via. No vendor name in either means you are reading your origin.
  2. Did the edge look in its cache for this document? Read the vendor status. DYNAMIC or NONE/UNKNOWN means no lookup.
  3. Did this response come out of storage? Read the status on a second GET. HIT, STALE and UPDATING qualify. MISS twice does not.
  4. Which edge answered? Read the cf-ray suffix, X-Amz-Cf-Pop or the x-served-by colo code, and compare it with where your audience is.

What it cannot tell you. It reads one URL, from one place, at one moment, logged out. Each data centre caches separately, so it says nothing about another continent, and a signed-in request is a different cache key.

Start With the Scan, and Check by Hand Second

Check D5, “CDN edge serves the page”, answers this for any URL in one pass. It carries 3 points of the delivery dimension, and a scan run on 4 October 2026 returned a pass with the evidence Cloudflare answered this page from its edge (cf-cache-status: HIT). It is free and needs no account.

Run the free scan on your own site: xspeedcache.com/scan/

By hand, the two-request GET above is the method. Connecting Cloudflare to WordPress covers the setup it checks, our Cloudflare guide says which parts of that panel are free and which are Pro, and Free vs Pro is the full split.

Reading the Receipt Across Twenty-Five Client Sites

Four headers per page is fine for one site and unworkable for fifty, where the question becomes which site stopped being cached. That is monitoring rather than a curl job, and xSpeed Hub is our own fleet console for xSpeed Cache sites: cache state and hit ratio per site in one view. The origin half is worth standardising too, since a page cache that names its layer makes the second verdict a read. Checking whether WordPress caching works covers it.

Common Mistakes People Make Proving a CDN Works

  • Reading an asset and reporting on the document. A HIT on style.css is the default and proves nothing about a page.
  • Testing with one request. A MISS stores the page. The second request is the measurement.
  • Trusting Age alone. Absence proves nothing and presence can be your origin’s own value.

Frequently Asked Questions

I turned on Cloudflare and cf-cache-status still says DYNAMIC on every page. What did I miss?

Nothing. This is the documented default: HTML is not on the extension list, so the document is not eligible until a cache rule says so. Cloudflare APO against an origin page cache compares the two routes.

I added a cache rule for HTML and now I see BYPASS instead. Is that progress?

Yes, and a different problem. DYNAMIC meant nothing looked. BYPASS means the lookup happened and your origin refused it, usually with a Set-Cookie or a no-store.

My curl -I says BYPASS but the page looks cached in a browser. Which one is right?

The browser, probably. curl -I sends a HEAD, and both Cloudflare’s rules and an origin cache can treat a non-GET as uncacheable. Re-run as a GET with the body discarded.

I get MISS every single time I check. What am I doing wrong?

Check whether the URL carries a query parameter, and whether you are signed in. Both change the cache key, so every request is a first one. Our cache exclusions guide covers the fields involved.

Do I still need a page cache on the origin if the edge is serving?

Yes, for every request the edge cannot serve: the first visitor in each region, signed-in sessions, anything excluded. xSpeed Cache, WP Rocket and LiteSpeed Cache all do it, and ours is free.

My scan says a CDN was detected but the edge is not serving. Is it contradicting itself?

No. Detection and delivery are separate findings, and separate lines in the report.

The Age on my page is 400,000 seconds. Should I purge?

Only if the content changed. A large Age on a HIT means the edge held a good copy for a long time, which is the point.

I cannot prove the edge is answering for a logged-in user.

Read the headers on a signed-in request and you will usually find a BYPASS. That is correct, and cached pages showing logged-in content is the failure you would rather not have.

My two probes disagree, one HIT and one MISS, within a minute.

Two data centres answered, each with its own copy. Compare the cf-ray suffix or x-served-by colo code and read them as two edges, not one flapping one.

I fixed the edge and my Largest Contentful Paint did not move.

Expected. This moves the first byte, a share of Largest Contentful Paint and not the whole of it. The split is in our CDN adoption study above.

Conclusion: Two Headers, Two Requests, One Real URL

If you want toRead thisOn
Know whether an edge answeredThe vendor status wordA second GET to a real URL
Know whose fault an uncached page isDYNAMIC against BYPASSThe same response
Know which edge answeredcf-ray, X-Amz-Cf-Pop or x-served-byBoth probes, compared

What to do this week: run the two-request GET on one real post URL, not the home page. Write down the status word and whether an Age came with it. On DYNAMIC, add a cache rule for your document. On BYPASS, find the Set-Cookie. Then scan the same URL and check that D5 agrees. If the origin half is what you cannot read, xSpeed Cache is free on wordpress.org and prints a layer-named receipt header; Pro is $29 a year at the founding price with a 14-day money-back guarantee, and the pricing page has the tiers.

If your two headers disagree in a way this does not cover, write it up. The vocabularies keep diverging.

Written by

xSpeed Cache Team

Try xSpeed Cache

Make your site load in milliseconds.

One switch. Zero bloat. Always free to start.