上周刚上线的钢材加工生产管理系统,运营同事急吼吼找来:坯料库的模型图全裂了,只剩个破碎图标,工人没法核对坯料型号。这已经是本月第三次静态资源出问题——前两次分别是 GIF 动图和系统图标 404,这次直接卡住了核心业务流程。
我先没动代码,开了浏览器控制台看网络请求。所有 /assets/ 开头的请求状态码都是 200,但响应体是一整页 index.html,Content-Type 也变成了 text/html,浏览器没法把它当图片解。一开始我怀疑前端打包配置,翻了 webpack 的 publicPath、Vite 的 base,本地环境跑得好好,只有生产服务器出毛病,锅肯定在反向代理那层。翻出 Nginx 配置后根因清楚了:原来只有一行兜底规则try_files $uri $uri/ /index.html;,意思是请求的资源不存在就重定向到 index.html。可前端打包后所有静态资源都进了 /assets/ 目录,Nginx 根本没单独处理这个路径,于是所有 /assets/ 请求都被当成「不存在的路径」,直接吐回 index.html,图片自然加载失败。
修复思路很清楚:在 server 块里给 /assets/ 单独开一条匹配规则,优先拦下静态资源请求,别让兜底规则碰它。生效配置是这样:
location /assets/ { alias /usr/share/nginx/html/assets/; expires 30d; add_header Cache-Control "public, immutable"; }这里我踩了个很典型的坑:第一版顺手写的是root /usr/share/nginx/html/assets/;,Nginx 拼完路径变成 /usr/share/nginx/html/assets/assets/xxx,又 404 了,排查了 10 分钟才反应过来——alias 是直接替换掉匹配到的路径前缀,root 是在原路径后面拼子路径,静态资源映射得用对指令。
配置确认无误,按流程npm run build:prod重新打包前端、打新 Docker 镜像,正要部署,SSH 连服务器超时了,运维说是机房网络波动,短时间恢复不了。我没干等,先把操作步骤和回滚方案整理成标准文档:停旧容器、加载新镜像、启动后跑nginx -t检查配置语法、没问题再重载 Nginx。同时把改好的 Nginx 配置、Dockerfile、部署脚本都备份进代码仓库。网络一恢复照着文档走,10 分钟部署完,图片恢复正常。
回头看,这事给了几条提醒:碰到静态资源 404,先开控制台看响应内容,如果返回的是 HTML 而不是资源本身,十有八九是 Nginx/Caddy 这类代理的配置问题,别上来就改前端;前端打包后带 hash 的静态资源一定要单独写 location 块,别光靠 try_files 兜底,否则会被当动态路由甩去 index.html,顺手加长期缓存还能提速;生产改配置前先把回滚方案备好,真遇上网络中断、权限不足也不至于拉长故障时间。