diff --git a/core/framework/server/app.py b/core/framework/server/app.py index 1e7e0f17..71dbc0e7 100644 --- a/core/framework/server/app.py +++ b/core/framework/server/app.py @@ -140,6 +140,25 @@ async def cors_middleware(request: web.Request, handler): return response +@web.middleware +async def no_cache_api_middleware(request: web.Request, handler): + """Prevent browsers from caching API responses. + + Without this, a one-off bad response (e.g. the SPA catch-all leaking + index.html for an /api/* URL before a route was registered) can get + pinned in the browser's disk cache and replayed forever, since our + JSON handlers don't emit ETag/Last-Modified and browsers fall back + to heuristic freshness. + """ + try: + response = await handler(request) + except web.HTTPException as exc: + response = exc + if request.path.startswith("/api/"): + response.headers["Cache-Control"] = "no-store" + return response + + @web.middleware async def error_middleware(request: web.Request, handler): """Catch exceptions and return JSON error responses. @@ -268,7 +287,7 @@ def create_app(model: str | None = None) -> web.Application: Returns: Configured aiohttp Application ready to run. """ - app = web.Application(middlewares=[cors_middleware, error_middleware]) + app = web.Application(middlewares=[cors_middleware, no_cache_api_middleware, error_middleware]) # Initialize credential store (before SessionManager so it can be shared) from framework.credentials.store import CredentialStore