From 2ff63e53560a78b9db147ffa6f48e30ac9040abb Mon Sep 17 00:00:00 2001 From: dvcdsys Date: Thu, 13 Aug 2026 13:35:25 +0100 Subject: [PATCH] fix(site): stop a missing asset from poisoning caches with HTML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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/.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 --- .gitignore | 3 ++ site/public/404.html | 74 ++++++++++++++++++++++++++++++++++++++++++++ site/vite.config.js | 12 +++++++ 3 files changed, 89 insertions(+) create mode 100644 site/public/404.html diff --git a/.gitignore b/.gitignore index 58efa0e7..ecb29795 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,9 @@ mac/dist/ # Marketing site (site/) build artifacts site/node_modules/ site/dist/ +# `wrangler pages dev site/dist` — used to reproduce Cloudflare Pages' header +# and not-found behaviour locally — drops its state here. +.wrangler/ # Dashboard build output — produced by `make dashboard-build`. # A committed `.gitkeep` keeps dist/ non-empty so `//go:embed all:dist` works # on a fresh clone (the embed.FS needs at least one entry). The real diff --git a/site/public/404.html b/site/public/404.html new file mode 100644 index 00000000..ba33b263 --- /dev/null +++ b/site/public/404.html @@ -0,0 +1,74 @@ + + + + + + + Not found · cix · CodeIndeX + + + + + + +
+

404

+

Nothing indexed at this path

+

The page you asked for does not exist. If you landed here from a link on the site, it is a bug worth reporting.

+ Back to codeindex.app +
+ + diff --git a/site/vite.config.js b/site/vite.config.js index 494e320c..57b7b040 100644 --- a/site/vite.config.js +++ b/site/vite.config.js @@ -12,6 +12,18 @@ const here = dirname(fileURLToPath(import.meta.url)); export default defineConfig({ plugins: [react()], build: { + // Cache generation, bumped to evacuate a poisoned URL. On 2026-08-10 a + // deploy window answered requests for /assets/*.js with index.html and a + // 200, and public/_headers stamped that HTML with the year-long immutable + // Cache-Control meant for hashed bundles — so every browser that hit the + // site during the window pinned "HTML that claims to be JavaScript" until + // 2027 and rendered a blank page. Content hashes cannot rescue those + // clients (the poisoned entry is keyed by the URL, and the URL was right), + // so the whole directory moves instead; index.html is served + // must-revalidate and picks the new paths up immediately. + // public/404.html now makes a missing asset a real 404, which is what + // stops this from recurring — bump this only if it somehow happens again. + assetsDir: 'assets/g2', rollupOptions: { input: { main: resolve(here, 'index.html'),