From a3378def5004729fbfd4ca4e1168c5ab97119b22 Mon Sep 17 00:00:00 2001 From: randogoth Date: Sat, 26 Sep 2026 20:42:42 +0300 Subject: [PATCH] fix: send Cache-Control so a stale Nix mtime can't pin visitors to old JS --- bridge.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/bridge.py b/bridge.py index 465d52d..6cd241b 100755 --- a/bridge.py +++ b/bridge.py @@ -96,8 +96,17 @@ async def relay(request: web.Request) -> web.WebSocketResponse: # The client loads no inline script, no inline style and no foreign origin, so a # strict policy costs nothing. form-action stays 'self' rather than 'none' so the # compose dialog's method="dialog" form is untouched. +# +# Nix normalizes every built file's mtime to the same fixed epoch for +# reproducibility, so Last-Modified is identical across every deploy — with no +# explicit Cache-Control, a browser's heuristic freshness calculation (based on +# that ~56-year-old timestamp) can treat a page as fresh indefinitely and never +# revalidate again, silently pinning a visitor to whatever JS they first +# loaded across every future deploy. no-cache forces a conditional GET each +# time rather than disabling caching outright — the ETag aiohttp already sends +# still turns an unchanged file into a bodyless 304. @web.middleware -async def csp(request: web.Request, handler): +async def headers(request: web.Request, handler): response = await handler(request) response.headers.setdefault("Content-Security-Policy", "; ".join([ "default-src 'self'", @@ -107,6 +116,7 @@ async def csp(request: web.Request, handler): "object-src 'none'", "frame-ancestors 'none'", ])) + response.headers.setdefault("Cache-Control", "no-cache") return response @@ -128,7 +138,7 @@ def main() -> int: args = parser.parse_args() logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") - app = web.Application(middlewares=[csp]) + app = web.Application(middlewares=[headers]) app["web_dir"], app["relay_ports"] = args.dir, set(args.allow_ports) app["allow_origins"] = set(args.allow_origins) app.router.add_get("/", index)