fix(site): stop a missing asset from poisoning caches with HTML - #244
Merged
Conversation
Cloudflare Pages answers a request that matches no built file by serving the root index.html with a 200. public/_headers stamps everything under /assets/* with `max-age=31536000, immutable`, so during the 2026-08-10 deploy window that combination handed browsers HTML pretending to be JavaScript and told them to keep it until 2027. The module then failed its MIME check, React never mounted, and codeindex.app has been a blank page ever since for everyone who visited during the window — a hard reload was the only cure, and content hashes could not help because the poisoned entry is keyed by a URL that was, and still is, correct. Two changes, one for each half of the problem: - public/404.html makes a missing path a real 404. Pages serves it with `Cache-Control: no-store`, which overrides the immutable rule, so the failure can no longer be cached at all. This is the fix that stops it recurring. - build.assetsDir moves the bundles to /assets/g2/, evacuating the poisoned URLs so already-affected visitors recover on their next visit: index.html is served must-revalidate and points at the new paths. Verified with `wrangler pages dev site/dist`, which applies _headers and the real not-found behaviour: /assets/g2/<missing>.js is now 404 + no-store, the hashed bundles still get the immutable header, and both the landing and docs pages render under the production CSP. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The outage
codeindex.app has been serving a blank page since the 2026-08-10 deploy window (the #237…#240 merges, ~22:20 UTC) — but only for people who visited during that window. The site itself is healthy: a first-time visitor today sees it fine.
Cloudflare Pages answers a request that matches no built file by serving the root
index.htmlwith a 200 (/assets/nope.js→200 text/html).public/_headersstamps everything under/assets/*withmax-age=31536000, immutable. During the deploy window, requests for/assets/*.jshit that combination: browsers were handed HTML pretending to be JavaScript and told to keep it, unrevalidated, until 2027. The module then fails its MIME check ("Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of text/html"), React never mounts,#rootstays empty.Measured on the live domain:
fetch(url, {cache: 'force-cache'})text/html,age: 223408(62 h) — the poisoned entryfetch(url, {cache: 'reload'})application/javascript— the network is correctA hard reload cures one browser. Nothing cures the rest, because
immutableis never revalidated and the bundle filenames did not change — content hashes cannot help when the poisoned entry is keyed by a URL that was, and still is, correct.The fix
public/404.html— a missing path is now a real 404, which Pages serves withCache-Control: no-store, overriding the immutable rule. The failure can no longer be cached at all. This is the half that stops it recurring.build.assetsDir: 'assets/g2'— the bundles move to/assets/g2/…, evacuating the poisoned URLs so already-affected visitors recover on their next visit:index.htmlis servedmust-revalidateand already points at the new paths.Also gitignores
.wrangler/, the state dirwrangler pages devdrops.Verification
Ran against
wrangler pages dev site/dist, which applies_headersand the real not-found behaviour:/assets/g2/<missing>.js→404+Cache-Control: no-store(was200 text/html+immutable)/assets/g2/main-<hash>.js→200 application/javascript+immutable— the/assets/*rule still covers the nested dir/docs/both render under the production CSP; the 404 page renders standalone (no bundle, no webfont — it has to work when the assets are the broken thing)🤖 Generated with Claude Code