chore(deploy): no-store the whole app path (per-HTML override didn't stick)

Cloudflare Pages let the /seg/* no-cache rule win over the per-HTML no-store, so
the index was still served no-cache. no-store the whole app path instead — the
shell is tiny and media is on R2 (separate, immutable). Guarantees a fresh index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-30 14:20:53 -07:00
parent ecd444b53b
commit 8c63b14967
+14 -12
View File
@@ -70,18 +70,20 @@ def _write_redirects(out: Path, seg: str) -> None:
def _write_headers(out: Path, seg: str) -> None:
# 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))
# returning visitors for hours (Pages' default is max-age=14400). `no-store`
# (never stored → a guaranteed-fresh fetch every visit) for the whole app path:
# `no-cache` (revalidate) was NOT enough — Safari served a STALE index from its
# disk/bfcache, loaded the OLD versioned JS, and broke the page (black, silent).
# A per-HTML no-store override didn't stick (Pages let the `/seg/*` rule win), so
# we no-store the whole path. The app shell is tiny (~150 KB); the media is on R2
# (immutable + content-hash, a separate domain) and is unaffected. The content-
# hashed asset URLs (see _version_assets) remain as belt-and-suspenders for the
# case where Pages caches .js/.css at the edge by type and ignores _headers.
# Cloudflare Pages `_headers` syntax.
(out / "_headers").write_text(
f"/{seg}/*\n"
" Cache-Control: no-store\n"
)
def _version_assets(app_dir: Path) -> None: