fire-planner: SPA cache headers — index.html no-cache, hashed assets immutable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Browsers were caching the old index.html which still pointed at the pre-Wave-2 bundle hash. Hashed assets under /assets/ stay cacheable for a year (immutable), but index.html (and any SPA fallback) must revalidate every request so a fresh deploy is visible immediately.
This commit is contained in:
parent
727e0bed08
commit
2f95c891fa
1 changed files with 19 additions and 2 deletions
|
|
@ -166,17 +166,34 @@ class _SPAStaticFiles(StaticFiles):
|
||||||
|
|
||||||
`StaticFiles(html=True)` only serves index.html for *directories*,
|
`StaticFiles(html=True)` only serves index.html for *directories*,
|
||||||
not arbitrary not-found paths — that's not enough for a SPA.
|
not arbitrary not-found paths — that's not enough for a SPA.
|
||||||
|
|
||||||
|
Cache policy: Vite hashes assets (e.g. ``index-XjyVM1-C.js``) so they
|
||||||
|
are immutable and can be cached forever. ``index.html`` references
|
||||||
|
those by hash — if the browser caches a stale ``index.html`` it'll
|
||||||
|
keep loading the OLD bundle. Force ``index.html`` (and any non-hashed
|
||||||
|
response, including SPA fallbacks) to revalidate on every request.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def get_response(self, path: str, scope: Scope): # type: ignore[no-untyped-def]
|
async def get_response(self, path: str, scope: Scope): # type: ignore[no-untyped-def]
|
||||||
try:
|
try:
|
||||||
return await super().get_response(path, scope)
|
response = await super().get_response(path, scope)
|
||||||
except StarletteHTTPException as exc:
|
except StarletteHTTPException as exc:
|
||||||
if exc.status_code == 404:
|
if exc.status_code == 404:
|
||||||
index = Path(self.directory) / "index.html" # type: ignore[arg-type]
|
index = Path(self.directory) / "index.html" # type: ignore[arg-type]
|
||||||
if index.is_file():
|
if index.is_file():
|
||||||
return FileResponse(index)
|
return FileResponse(
|
||||||
|
index,
|
||||||
|
headers={"Cache-Control": "no-cache, must-revalidate"},
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
# Hashed Vite assets live under /assets/ and contain an 8-char
|
||||||
|
# hash — safe to cache aggressively. index.html and anything
|
||||||
|
# else (logos, favicons) revalidate.
|
||||||
|
if path.startswith("assets/") and "-" in path:
|
||||||
|
response.headers["Cache-Control"] = "public, max-age=31536000, immutable"
|
||||||
|
else:
|
||||||
|
response.headers["Cache-Control"] = "no-cache, must-revalidate"
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
# Mount the SPA last so it catches any path the API didn't claim.
|
# Mount the SPA last so it catches any path the API didn't claim.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue