From 6bfeb5a02a63b984b872bfda3f16a7db3b8d93ac Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 2 Oct 2026 17:22:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(caddy):=20=E8=A1=A5=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E7=AD=96=E7=95=A5=EF=BC=8C=E6=B6=88=E9=99=A4=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E9=83=A8=E7=BD=B2=E5=90=8E=E6=97=A7=20index.?= =?UTF-8?q?html=20=E6=8C=87=E5=90=91=E5=B7=B2=E5=88=A0=E9=99=A4=20chunk=20?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 前端产物文件名带内容 hash,index.html 不带,而 Caddyfile 里没有任何 Cache-Control。 Caddy 的 file_server 不设该头,浏览器遂按启发式缓存 index.html;一旦命中缓存, 前端重新部署后用户会拿着旧 index 去请求**已从镜像里删掉的旧 chunk**, 表现为后台页面打不开,只能手动强刷恢复(本次部署风控 tab 时就遇到了)。 策略: - /assets/*(文件名带 hash,内容变则名变)→ public, max-age=31536000, immutable - 其余静态响应(含 SPA 回退到 index.html 的)→ no-cache,每次校验 两个容易踩的点: 1. header 必须写在 file_server **之前**。route 内按书写顺序执行, 排在 file_server 之后响应头已写出,就改不动了。 2. SPA 回退(如 /admin/worker-platform)走的是 try_files 内部重写, 用 `header /index.html` 这种按请求路径匹配的写法**匹配不上**, 因此改为「排除 /assets 与接口前缀」来覆盖文档类响应。 接口前缀显式排除,避免覆盖后端自己设置的缓存头(handle 匹配后会终止链路, 这些规则本就不会落到 /api 上,显式排除只是防止日后有人在 handle 之外加规则时被误伤)。 验证(部署前用生产同款 caddy 镜像离线做的): - caddy validate → Valid configuration - 临时容器跑真实 snippet,实测响应头: /assets/foo.js -> public, max-age=31536000, immutable /index.html -> no-cache /admin/worker-platform -> no-cache --- deploy/caddy/Caddyfile | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/deploy/caddy/Caddyfile b/deploy/caddy/Caddyfile index 21ad4944..165f1cbc 100644 --- a/deploy/caddy/Caddyfile +++ b/deploy/caddy/Caddyfile @@ -18,6 +18,27 @@ reverse_proxy backend:3000 } + # 缓存策略。 + # + # 位置很关键:必须写在 file_server **之前**。route 内按书写顺序执行, + # header 若排在 file_server 之后,响应头已经写出,就再也改不动了。 + # (上面的 handle 匹配后会终止链路,所以这些规则不会落到 /api 等反代响应上; + # 下面仍显式排除接口前缀,避免日后有人在 handle 之外加规则时被误伤。) + # + # 背景:前端产物的文件名带内容 hash,但 index.html 不带。index.html 一旦被缓存, + # 前端重新部署后用户会拿着旧 index 去请求**已从镜像里删掉的旧 chunk**, + # 表现为后台页面打不开,只能靠手动强刷恢复(生产已遇到)。 + + # 带 hash 的构建产物:内容变则文件名变,可以长缓存并标记 immutable + @hashedAssets path /assets/* + header @hashedAssets Cache-Control "public, max-age=31536000, immutable" + + # 其余静态响应(含 SPA 回退到 index.html 的那些)一律每次校验 + @documentResponse { + not path /assets/* /api/* /health* /s/* + } + header @documentResponse Cache-Control "no-cache" + root * /srv try_files {path} /index.html file_server