大型SPA部署时404本质是构建产物非原子替换与缓存协同失效所致:需用软链接切换版本、资源带contentHash、Nginx配置try_files $uri $uri/ /index.html并禁用alias。

大型SPA应用在日志更新期间出现静态文件404,本质不是日志本身的问题,而是构建产物部署过程中的原子性缺失和缓存协同失效。这类问题多发于CI/CD高频发布、资源版本未对齐、或Nginx等服务器在文件替换窗口期响应了部分旧请求的场景。
构建产物需保证原子替换
大型SPA通常包含大量JS/CSS/图片等静态资源,构建输出(如dist目录)若被“边删边写”或覆盖式同步,会导致某次HTTP请求恰好命中一个正在被删除但尚未写入完成的文件路径,直接返回404。
- 推荐使用软链接切换方式:构建新版本到独立目录(如dist-v2.1.5),再通过原子操作更新指向当前版本的符号链接(如dist-current → dist-v2.1.5)
- 避免rsync -r或cp -r直接覆盖目标目录;改用rsync --delete-after + --link-dest实现增量硬链接复用,减少IO窗口期
- 在Kubernetes等环境中,用initContainer校验dist目录完整性(如检查index.html是否存在且非空)后再触发服务就绪探针
静态资源路径必须带内容哈希
不带hash的文件名(如app.js、main.css)极易因浏览器或CDN缓存未及时刷新,导致新HTML引用了旧文件名,而该文件在新版本中已被重命名或移除,从而404。
- Webpack/Vite等工具开启contentHash(如[contenthash:8]),确保文件内容变则文件名变
- HTML中引用的资源必须由构建工具自动注入,禁用手工写死路径
- 配合Cache-Control: immutable响应头,向CDN和浏览器声明该资源永不失效,避免条件请求干扰
Nginx需启用try_files并规避alias陷阱
即使资源路径正确,若Nginx配置不当,仍会在文件短暂缺失时返回404而非回退到index.html——尤其在高并发部署瞬间。
- 务必使用root而非alias定义静态根目录;alias易因路径拼接错误导致$uri解析失败
- location / 配置中明确写:try_files $uri $uri/ /index.html;,注意顺序不能颠倒(先查文件,再查目录,最后兜底)
- 对已知静态资源后缀(.js, .css, .png等)单独设置location块,并添加expires 1y和add_header Cache-Control "public, immutable"
日志滚动本身不触发404,但可能掩盖真实诱因
所谓“日志更新期间”出问题,往往是误判。真正的触发点通常是:日志轮转脚本意外清除了dist目录(如误配logrotate)、监控脚本自动清理“旧构建目录”时删错了软链接目标、或运维手动执行了rm -rf dist/*却未等CI完成。
- 检查Nginx error log中404的具体URI,确认是/index.html缺失,还是某个chunk.js缺失——前者是路由兜底失效,后者才是构建/部署问题
- 在CI流程末尾增加校验步骤:curl -I http://localhost:8080/static/js/app.[hash].js | grep "200 OK"
- 禁止任何自动化脚本对部署目录执行无保护的rm -rf;改用mv old_dist_$(date +%s) + rsync --delete-excluded


















