(order-site) { route { # 必须放在 handle 之前:Caddy 的 route 按书写顺序执行,而 handle 匹配后即终止链路, # 放在后面会让**所有 API 响应都跳过压缩**(生产实测订单列表 75.9KB 明文下发, # 客户端已发 Accept-Encoding: gzip, br)。静态文件之所以一直是压缩的, # 是因为静态文件落在 encode 之后的 file_server。SSE 响应不会被压缩(见 encode 默认规则)。 encode zstd gzip handle /api/* { reverse_proxy backend:3000 } handle /health* { reverse_proxy backend:3000 } handle /s/* { 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 } } {$CADDY_SITE_ADDR:http://localhost} { import order-site } {$WORKER_SITE_ADDR:https://worker.khhao.com} { import order-site } live.khhao.com { reverse_proxy douyu-login:8800 } mall.khhao.com { reverse_proxy 172.17.0.1:8090 }