静态资源匹配必须用含hash的正则,HTML需禁用缓存,proxycache不适用带hash资源。应使用location ~* /[a-zA-Z0-9-]{6,}.(js|css|png|jpg|gif|svg|woff2?|ttf|eot)$匹配资源,/和.html单独配置no-store,避免proxy_cache处理静态文件。

静态资源匹配必须用正则,不能只靠后缀
很多人直接写 location ~* \.(js|css)$,看似覆盖了所有 JS/CSS,但实际会漏掉带 hash 的文件名(比如 app.a1b2c3.js)。浏览器请求的是带 hash 的完整路径,Nginx 必须能精准匹配它,否则无法命中缓存规则,更谈不上控制 Cache-Control。
正确做法是把 hash 段也纳入正则:用 [a-f0-9]{8,} 或更宽松的 [a-zA-Z0-9_-]{6,} 匹配常见构建工具生成的 hash 长度。Vite 通常用 8 位,Webpack 默认是 20 位,Vue CLI 可能带下划线或短横线。
- 推荐写法:
location ~* /[a-zA-Z0-9_-]{6,}\.(js|css|png|jpg|gif|svg|woff2?|ttf|eot)$ - 如果资源统一放在
/static/或/assets/下,优先用前缀 + hash 正则,比全路径模糊匹配更安全 - 避免用
.*\.(js|css)—— 它可能意外匹配到/api/js这类非静态接口,引发严重 fallback 错误
HTML 入口必须禁用缓存,且不能依赖 try_files fallback
index.html 是唯一不带 hash 的关键文件。只要它被缓存,里面引用的带 hash 资源路径就固定了,下次发版后用户仍会尝试加载已删除的旧文件,直接 404 或白屏。
重点不是“怎么 fallback”,而是“怎么确保 HTML 永远不进缓存”。try_files $uri /index.html 只解决路由回退,对缓存毫无约束力。
立即学习“前端免费学习笔记(深入)”;
- 根路径必须单独配置:
location = / { add_header Cache-Control "no-cache, no-store, must-revalidate"; } - 所有 HTML 文件也要兜底:
location ~* \.html$ { add_header Cache-Control "no-store"; } - 不要在 HTML location 块里写
try_files—— 它和缓存头无关,反而可能干扰响应逻辑
proxy_cache 不适用于带 hash 的静态资源
有人想用 Nginx 的 proxy_cache 统一管理前端资源,这是典型误用。带 hash 的 JS/CSS 本质是不可变内容,应由浏览器直连 Nginx 获取并强缓存;而 proxy_cache 是为动态响应设计的,用于缓存后端接口返回内容。
若强行开启 proxy_cache 处理 app.a1b2c3.js,会导致两个问题:一是多一层代理转发开销,二是缓存键(proxy_cache_key)若没包含完整 URI,容易把不同 hash 的同名资源混存;三是 inactive 过期策略与 immutable 冲突,违背“URL 即版本”的前提。
- 带 hash 的资源:走纯静态服务,用
expires和add_header Cache-Control - 真正需要
proxy_cache的,是 API 接口(如/api/user),且需配合proxy_cache_valid控制状态码粒度的过期时间 - CDN 层才适合做 hash 资源的边缘缓存,Nginx 在这里只需透传响应头
上线后验证要盯死三类响应头
改完配置别急着重载,先确认你真正关心的三个位置是否生效。很多问题不是配置错,而是验证方式不对——比如只看 Network 列表,却没点开具体请求的 Response Headers 面板。
-
index.html响应头中必须含Cache-Control: no-store或no-cache,且不能出现Expires字段 - 一个带 hash 的 JS 请求(如
app.a1b2c3.js)响应头中必须同时有Cache-Control: public, immutable和Expires为 1 年后的时间戳 - 任意一个未命中静态规则的路径(如
/user/profile)应返回 200 +index.html内容,且响应头不含任何缓存指令(即继承 root location 的默认行为)
真正容易被忽略的是:Nginx 配置 reload 后,旧 worker 进程可能还在处理请求,导致响应头看起来没更新。务必用 curl -I http://localhost/xxx 直连验证,绕过浏览器中间层干扰。


















