The default that bit us
Cloudflare caches by file extension. A stylesheet is held at the edge for hours because, for almost every site on the internet, a stylesheet does not change. That guess is correct often enough to be a sensible default and wrong in a specific way for anyone shipping frequently: a styling fix deployed, the deploy succeeded, and the site kept serving the old stylesheet for hours.
The fix was to stop arguing with the default and satisfy it instead. Every page now links the stylesheet with a content hash in the URL, so a change to the file is a new cache key and the old entry is irrelevant rather than in the way. Nothing had to be purged and no server behavior changed.
The general shape is worth keeping. An extension-based rule is a proxy for a claim about change frequency. When your content is a marketing page, the proxy holds. When your product is a JSON document that is rewritten every morning, it does not, and the default will be confidently wrong on your most important file.
What we measured instead of assuming
Writing this note, two claims that felt obviously true did not survive a check, and both are more interesting than the post we would have written without checking.
The first: that a bot-protection default was turning agents away. It is not. Requests identifying as GPTBot, ClaudeBot, PerplexityBot, Googlebot, Bytespider, curl and plain python all receive 200 from the edition endpoint. An earlier reading that suggested otherwise turned out to be our own HTTP client rejecting a user-agent string before it ever sent a request.
The second: that the CDN was serving a stale edition document. It is not. The endpoint reports cf-cache-status DYNAMIC, carries no Age header, and returns byte-identical content with and without a cache-busting query string. What we had actually seen was a deployment rollout still serving from the previous container, which looks exactly like a cache from the outside and is not one.
The real lesson was about where we looked
The CDN cost us hours once. Checking the wrong copy cost more, twice in one week, and neither had anything to do with the edge.
A CORS rule was added to a _headers file. The rule was correct, the review was clean, and the file is a Netlify and Cloudflare Pages convention that our origin does not read at all. Production serves from a small Node server that carries its own header table. The rule shipped and changed nothing, and the diff looked right the entire time.
The same blind spot hid a second fault. That server returns 404 for any file extension missing from its MIME table, and PNG was missing, so the social card we had just shipped returned 404 in production. Every link preview was broken from the moment it launched. Both were verified against the build output, which is the artifact we produced rather than the thing anyone actually receives.
What we changed
One source now generates both the origin server's headers and the _headers file, with a test asserting the two agree, that no per-path rule can quietly drop a security header, and that every rule's path pattern matches its own runtime matcher. The generated file stays because it is correct on hosts that read one, but it is no longer where the policy lives.
And verification moved to the deployed origin. A script now checks the live site rather than the build: the beacon and whether its age is plausible, CORS on the edition JSON, HSTS, the card actually served as image/png, HTML still same-origin, the archive index, the integrity head, and every URL our llms.txt publishes. It caught a deploy we had already called finished on its first run.
If there is a rule in this, it is not about Cloudflare. Defaults are someone else's guess about a reader you may not have, so find out which guess you inherited. And check the copy your reader receives, not the one you built.