diff --git a/backend/app/main.py b/backend/app/main.py index a1d0d1e..6b53aaa 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -208,9 +208,13 @@ def _mount_spa(app: FastAPI) -> None: full_path and _STATIC_DIR in candidate.parents and candidate.is_file() + and candidate.name != "index.html" ): + # Хэшированные ассеты Vite — обычное кэширование (имя меняется при сборке). return FileResponse(candidate) - return FileResponse(index_file) + # index.html (корень и SPA-маршруты) — НЕ кэшируем: иначе браузер отдаёт старый + # документ из кэша и при недоступности хоста не доходит до Caddy с заглушкой. + return FileResponse(index_file, headers={"Cache-Control": "no-store"}) app = create_app() diff --git a/deploy/vps/Caddyfile b/deploy/vps/Caddyfile index 678927f..e469638 100644 --- a/deploy/vps/Caddyfile +++ b/deploy/vps/Caddyfile @@ -12,12 +12,21 @@ # SSE (/api/events): отдельный handle БЕЗ encode и с flush_interval -1 — иначе сжатие/ # буферизация задержат поток событий (реал-тайм перестанет работать). # -# Туннель лёг (апстрим недоступен) → reverse_proxy отдаёт 502/503/504, и сниппет -# (offline) подменяет ответ на свою страницу-заглушку (/etc/caddy/maintenance/maintenance.html), -# вместо «зависшей загрузки» SPA. Файл заглушки — deploy/vps/maintenance.html. +# Туннель лёг (апстрим недоступен) → reverse_proxy отдаёт 502/503/504, и сниппет (edge) +# подменяет ответ на страницу-заглушку (/etc/caddy/maintenance/maintenance.html) вместо +# «зависшей загрузки» SPA. Тот же сниппет запрещает кэш HTML-документа (Cache-Control: no-store), +# иначе браузер отдаёт старый index.html из кэша и до Caddy/заглушки запрос не доходит. +# Файл заглушки — deploy/vps/maintenance.html. -# Заглушка «сайт недоступен» при недоступности апстрима. Импортируется в каждый сайт. -(offline) { +# Edge-поведение, общее для сайтов: запрет кэша HTML-документа + заглушка при падении апстрима. +(edge) { + # HTML-документ (навигации, Accept: text/html) НЕ кэшируем. Иначе браузер отдаёт старый + # SPA из кэша без сетевого запроса → запрос не доходит до Caddy и заглушку не видно. + # Хэшированные ассеты (JS/CSS) под это не попадают (у них другой Accept) и кэшируются как обычно. + @html header Accept *text/html* + header @html Cache-Control "no-store" + + # Туннель лёг (апстрим 502/503/504) → отдаём свою страницу-заглушку со статусом 503. handle_errors { @down expression `{http.error.status_code} in [502, 503, 504]` handle @down { @@ -43,7 +52,7 @@ forbiddenstars.ru { encode zstd gzip reverse_proxy 127.0.0.1:9000 } - import offline + import edge } forbidden-stars.ru { @@ -58,5 +67,5 @@ forbidden-stars.ru { encode zstd gzip reverse_proxy 127.0.0.1:9001 } - import offline + import edge }