diff --git a/tools/build_static.py b/tools/build_static.py index 041be8f..3888cdf 100644 --- a/tools/build_static.py +++ b/tools/build_static.py @@ -69,14 +69,19 @@ def _write_redirects(out: Path, seg: str) -> None: def _write_headers(out: Path, seg: str) -> None: - # The app shell (html/js/json) must NOT be browser-cached — otherwise a deploy is - # invisible to returning visitors for hours (Pages' default is max-age=14400). - # `no-cache` = store but revalidate every load (cheap 304 via etag). The media is - # on R2 (immutable + content-hash), unaffected. Cloudflare Pages `_headers` syntax. - (out / "_headers").write_text( - f"/{seg}/*\n" - " Cache-Control: no-cache\n" - ) + # The app shell must NOT be browser-cached — otherwise a deploy is invisible to + # returning visitors for hours (Pages' default is max-age=14400). The HTML entry + # points get `no-store` (never stored → a guaranteed-fresh index every visit): + # `no-cache` (revalidate) was not enough — Safari served a STALE index from its + # disk/bfcache, which then loaded the OLD versioned JS and broke the page. A fresh + # index is the linchpin: it carries the content-hashed asset URLs (see + # _version_assets), so once the HTML is fresh the JS/CSS are too. The rest of the + # app path stays `no-cache`. Media is on R2 (immutable + content-hash), unaffected. + # Cloudflare Pages `_headers` syntax; later rules win for the same header. + html_pages = ("", "index.html", "credits.html", "about.html") # "" = the /seg/ root + blocks = [f"/{seg}/*\n Cache-Control: no-cache\n"] + blocks += [f"/{seg}/{p}\n Cache-Control: no-store\n" for p in html_pages] + (out / "_headers").write_text("".join(blocks)) def _version_assets(app_dir: Path) -> None: