Skip to content

docs(pages): record which cache rules the edge overrides - #1203

Merged
TaprootFreak merged 2 commits into
developfrom
docs/headers-edge-override
Jul 29, 2026
Merged

docs(pages): record which cache rules the edge overrides#1203
TaprootFreak merged 2 commits into
developfrom
docs/headers-edge-override

Conversation

@TaprootFreak

Copy link
Copy Markdown
Collaborator

Follow-up to #1200, and the replacement for #1202, which was closed because its premise turned out to be wrong.

What is actually happening

Two rules in public/_headers do not reach the client as written. Measured against the deployed site with a cache-busting query, so none of this is a stale edge copy:

Path cf-cache-status rule in _headers actually served
/widget/v1.0.css MISS public, max-age=3600 public, max-age=14400
/robots.txt MISS no-cache, must-revalidate max-age=14400, must-revalidate
/favicon.ico, /logo.png MISS public, max-age=86400 as written
/static/js/<hash>.js MISS max-age=31536000, immutable as written
/manifest.json, /index.html DYNAMIC no-cache, must-revalidate as written
/widget/v1.0 DYNAMIC public, max-age=3600 as written

The rule is not "explicit numbers get through and no-cache does not". It is: for responses the edge caches, any max-age below four hours is raised to four hours. Values above it are untouched, and uncached responses pass through regardless. Cloudflare documents exactly this behaviour for the browser cache TTL setting — it overrides the origin header when the origin value is lower, and the default is four hours.

The two rows that show it are /widget/v1.0.css against /widget/v1.0: same rule, same asset family, different outcome purely because one is cached and the other is not.

Why this is a comment and not a fix

#1202 proposed writing public, max-age=0, must-revalidate for robots.txt, on the theory that an explicit number would be passed through. That theory came from favicon.ico and logo.png, whose 86400 does get through — but 86400 is above four hours. 0 is below it and would be raised to 14400 just as no-cache is. The change would have looked applied while doing nothing, which is the failure this file should not contain. It was closed unmerged.

The four hours come from CDN configuration, outside this repository. Nothing written in public/_headers can lower it. What this file can do is say so, and say how to check — so the next person reading the /widget/* rule knows that the hour it asks for is not the hour clients get, and does not spend an afternoon rediscovering it.

Scope

Comment lines only. No rule, path or header value is touched — every added line starts with #, and nothing is removed.

Two rules in this file do not reach the client as written. For responses the
edge caches, any max-age below four hours is raised to four hours: /widget/*
asks for one hour and /robots.txt for revalidation, and both arrive as
max-age=14400. Values above four hours are untouched, which is why the day
of cache on favicon.ico and logo.png does apply, and uncached responses pass
through, which is why no-cache works for index.html and manifest.json.

The value comes from CDN configuration and cannot be changed here. Writing
max-age=0 instead of no-cache would not help either -- it is below four hours
and would be raised the same way.

So the note records the limit rather than pretending the rules hold. A rule
that reads as applied and is not is worse than one that says where it stops.
The note explained the override and proved it on robots.txt, but the clearest
evidence sits under the widget rule: /widget/v1.0.css is cached and reaches
the client as max-age=14400, while /widget/v1.0 is not cached and keeps the
max-age=3600 the rule asks for.

Same rule, two outcomes, decided only by whether that particular response is
cached -- which is the point the note is making.
@TaprootFreak

Copy link
Copy Markdown
Collaborator Author

Two review passes over two lenses — project conformity and logic/correctness — both ending at zero findings. The first pass is where the widget evidence came from: the note originally proved the override only on robots.txt, and the reviewer pointed out that the clearest demonstration was sitting unused under the widget rule. That comparison is now in the file, which is the second commit.

The verification instruction in the note was run rather than assumed: curl -sI "https://app.dfx.swiss/robots.txt?cb=1" returns max-age=14400, must-revalidate with cf-cache-status: MISS, and the widget pair behaves as described.

One check worth recording: /asset-manifest.json carries the same no-cache rule as robots.txt and is not affected, because it comes back DYNAMIC rather than cached. All nine rules in the file were walked through; /widget/* and /robots.txt are the only two that ask for less than four hours, so the note's list is complete.

One option this PR does not take. Cache-Control: no-store for robots.txt might sidestep the override entirely — if the edge does not store the response at all, there is nothing for the browser cache TTL to raise, and the header would pass through the way it does for the DYNAMIC entries. That is a plausible reading of the same evidence, not a measured fact, and confirming it needs a deploy. It also costs the conditional-request benefit and would be wrong for /widget/*, where caching is the point of the rule. Given that this PR exists precisely because the previous attempt shipped an unverified assumption, it seemed better to name the option here than to write it into the file. Happy to test it if the four-hour window on robots.txt is worth removing.

@TaprootFreak
TaprootFreak marked this pull request as ready for review July 28, 2026 14:56
@TaprootFreak
TaprootFreak merged commit 3aa225d into develop Jul 29, 2026
6 checks passed
@TaprootFreak
TaprootFreak deleted the docs/headers-edge-override branch July 29, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant