关键靠nginx rewrite+last在入口做内部路径重写:url不变,后端取新位置文件;需匹配location、仅处理uri、不跨server;必须用last触发重新匹配,配合try_files防404。

要让静态资源版本升级对用户和前端代码完全透明,关键不是改 HTML 引用或清 CDN 缓存,而是靠 Nginx 的 rewrite + last 在请求入口做内部路径重写——URL 不变,后端取的却是新位置的文件。
明确 rewrite 的生效前提
rewrite 不是全局生效的,它只在能匹配到原始请求路径的 location 块内起作用。比如用户请求的是 /dist/main.js,那你就得先有 location /dist/ { ... } 这样的配置块,否则 rewrite 根本不会触发。
- 只处理 URI 路径部分,查询参数(如
?v=1.2.3)原样保留 - 不能跨 server 块生效,必须落在当前请求所匹配的 server 和 location 中
- 推荐把 rewrite 放在专门处理静态资源的 location 里,避免和 API 路由混在一起
用 last 实现真正的内部重写
flag 选错会导致跳转暴露、循环匹配或规则失效。静态资源迁移必须用 last,它会让 Nginx 拿重写后的新 URI 重新走一遍 location 匹配流程,从而进入真正服务文件的位置(比如 root 或 alias 所在的 location)。
-
rewrite ^/dist/(.*)$ /$1 last;:把所有/dist/xxx请求内部映射到根路径下的/xxx -
rewrite ^/static/v1/(js|css|img)/(.+)\.[a-f0-9]{6,}\.(js|css|png)$ /$1/$2.$3 last;:提取类型、文件名主体和后缀,抹掉哈希值,适配 Webpack 构建产物 - 绝对不要用
redirect或permanent,它们会发 302/301,破坏缓存、暴露路径、增加延迟
搭配 try_files 防止 404 断档
rewrite 是主动映射,但新资源可能还没完全发布到位。加一层 try_files 就像设了个保险阀:先按重写后的路径找文件,找不到就回退到旧路径或返回 404。
location /dist/ { rewrite ^/dist/(.*)$ /$1 last; }-
location / { root /var/www/html; try_files $uri /dist/$uri =404; }:优先找新路径,失败再查旧路径 - 这种组合让上线过程变成“先部署新资源 → 更新 Nginx 配置 → 观察日志 → 确认无误后删旧目录”,全程用户无感知
处理常见结构变更场景
不同项目升级方式不同,rewrite 可以灵活应对:
- 从
/build/迁移到根目录:rewrite ^/build/(.*)$ /$1 last; - 统一去除前缀
/legacy/:rewrite ^/legacy/(.*)$ /$1 last; - v1 升 v2 版本目录:
rewrite ^/static/v1/(.*)$ /static/v2/$1 last; - 多个旧前缀合并处理(配合 map 更清晰):
map $uri $new_uri { ~^/old1/(.*)$ /$1; ~^/old2/(.*)$ /$1; default $uri; },再用rewrite ^ $new_uri last;










