A static build decides what HTML exists before any request arrives. It does not decide whether Cloudflare keeps a copy of that HTML at the edge. On Cloudflare Pages the default is that it does not, and a page on a fully static site answers cf-cache-status: DYNAMIC unless someone has changed that.

It is easy to treat “static” and “cached” as the same word. They name two different systems, and the gap between them usually surfaces the same way: a site audit flags a page as slow, the page is fast every time someone checks it by hand, and nobody can reproduce the number.

What output: 'static' actually controls

Take Astro as the example. Its output: 'static' prerenders every page at build time unless a route opts out.[1] That is a build instruction, and what it produces is files. What happens to those files in transit (which network holds a copy, for how long, and whether a given request gets served from it) is a separate system with its own configuration. The word “static” in the build setting does not reach into it.

The same is true of every static-site generator. They build files. The host decides what gets cached.

What the check shows

A request for any HTML page on a default Pages deployment comes back with two revealing headers:

$ curl -sI https://example.com/blog/some-post/ | grep -i '^cache-control\|^cf-cache-status'
cache-control: public, max-age=0, must-revalidate
cf-cache-status: DYNAMIC
Illustrative. A default Cloudflare Pages deployment, asked for an HTML page

Ask the same site for an image on that page and the answer is different:

$ curl -sI https://example.com/images/hero.png | grep -i '^cache-control\|^age\|^cf-cache-status'
cache-control: public, max-age=604800
age: 1354
cf-cache-status: HIT
Illustrative. The same deployment, asked for an image on the same page

The image is cached, the stylesheet is cache-eligible and warms up to HIT under traffic, and the HTML is the one thing on the page that is never eligible at all. A page like that is usually quick when warm. Fetched from a location that has no warm copy, it is slower, and a single-sample crawler is exactly the tool that lands on a bad draw and reports it.

”Static” and “cached” are different systems

Cloudflare’s CDN does not cache HTML by default.[2] That holds on Pages and everywhere else. HTML and JSON sit outside the default cacheable set, and everything else a normal page pulls in (CSS, JS, fonts, images) sits inside it. So on a “fully static” site the assets get a CDN cache and the documents do not.

DYNAMIC is the status Cloudflare returns when it decides, at request time, that a response is not eligible for cache and skips the cache lookup entirely.[3] Pages then serves the request directly. Pages is not a single origin server: uploaded assets sit in a tiered store with a one-week TTL and can be evicted from any given location at any time.[4] So a DYNAMIC HTML response is usually still quick, because it is a read from a store near the user rather than a trip back to one server. But it is a read, not a hit. A location without a warm copy does that read cold, and that is the number a crawler reports.

Pages also states the policy in the response itself: every asset carries Cache-Control: public, max-age=0, must-revalidate.[4] A common override gives fingerprinted build assets a year, since their filenames change whenever their bytes do. Left alone, HTML keeps max-age=0, must-revalidate, which tells browsers and every proxy in between to keep nothing and check every time.

What buying the host covered, and what it did not

The case for a managed static host is that delivery stops being your problem, and most of it does. You stop running and patching a web server, and you get TLS, global distribution, DDoS absorption and atomic deploys for the price of a git push. For most marketing sites that is a good trade.

What you keep is the cache policy for your own HTML, and nobody hands it to you. The default makes sure a visitor never sees a stale page, and to do that it gives up serving any page from cache. The trade stays invisible until something measures it, because “static hosting” sounds like caching is already settled. What the host settled is distribution. Caching the documents themselves is still a decision, and until you make it, the platform has made it for you, on the conservative side.

Turning it on, and what that costs

Making HTML eligible for the edge cache takes a Cache Rule: mark the response cacheable and give it an edge TTL. That is the supported way to cache something Cloudflare would otherwise skip.[5] For a marketing site, a short edge TTL plus stale-while-revalidate is the setup that fits. A few minutes of edge TTL turns repeat requests into a HIT, and a day of stale-while-revalidate covers the moment while a fresh copy is fetched in the background.

The cost lands on deploys. HTML paths stay the same across releases (/blog/x/ is the same URL before and after), so once the edge holds a copy, a deploy’s change to that page stays invisible until the TTL runs out or you purge. Fingerprinted assets avoid this because their filenames change with their bytes, and HTML gets no such protection. Edge-caching HTML therefore makes every deploy depend on a prompt purge, and a forgotten purge looks exactly like a deploy that silently failed.

The second cost is scope. A blanket rule is wrong on the day the site gains a route it should never have touched. The _headers file is not applied to responses from Pages Functions,[6] so on a site where some pages are generated by Functions, a rule scoped through _headers skips them automatically. A zone-level Cache Rule matching /* would not, and the first personalised or authenticated HTML route added later would be served out of a shared cache. Whichever tool you use, the rule has to name exactly what it covers.

Leaving it off is also a legitimate answer. If the site is already fast enough that a cold read costs a couple of hundred milliseconds, and the list of things that would actually move the business is long, this may not be near the top of it. What matters is choosing that, not inheriting it.

The check, for your own domain

You get by defaultYou turn on yourselfHow you would know it is off
CDN cache for images, CSS, JS, fontsCDN cache for HTML, via a Cache Rule marking it eligible with an edge TTLcurl -sI shows cf-cache-status: DYNAMIC on a page and HIT on its images
public, max-age=0, must-revalidate on every assetA longer max-age on fingerprinted assets, via _headersfingerprinted build paths still answer max-age=0
Global distribution, TLS, DDoS absorptionNothing, this is the part the host genuinely ownsnot applicable
A near-user store read for uncached HTMLA cache HIT for HTMLno Age header on a page response, ever
Run against your own site. It takes under a minute.

One line answers it for any domain: curl -sI https://yourdomain/ | grep -i cf-cache-status. If it says DYNAMIC and you had assumed a static site was a cached one, it is not, and that is the default rather than a mistake. Whether it matters is a separate question, so measure the cold time to first byte from a few regions before you spend a change on it. That decision should be yours, not one the platform default made for you by leaving it alone.

Sources

  1. Configuration Reference - Astro , accessed

    Supports: Astro's output: 'static' prerenders every page at build time unless a route opts out; it is a build-time setting and does not govern how the resulting HTML is cached in transit.

  2. Default Cache Behavior - Cloudflare , accessed

    Supports: The Cloudflare CDN does not cache HTML by default; static file types such as CSS, JS, fonts and images are in the default cacheable set and HTML is not.

  3. Cloudflare cache responses - Cloudflare , accessed

    Supports: A cf-cache-status of DYNAMIC means Cloudflare determined at request time that the response is not eligible for cache and sent the request on without a cache lookup; HIT means it was served from Cloudflare's cache.

  4. Serving Pages - Cloudflare , accessed

    Supports: Cloudflare Pages serves cacheable asset responses with Cache-Control: public, max-age=0, must-revalidate, and uploaded assets have a one-week TTL and can be evicted from a location at any time.

  5. Cache Rules - Cloudflare , accessed

    Supports: Cache Rules adjust what is eligible to cache, how long it is cached, and where, which is the supported way to cache a response type Cloudflare would not cache by default.

  6. Headers - Cloudflare , accessed

    Supports: The _headers file overrides response headers on static asset responses but is not applied to responses generated by Pages Functions.