fix: SPA 路由回退的 HTML 补 no-cache 头(配合 #1732 白屏修复) #1735

Merged
auto-approve-bot merged 1 commits from fix/nginx-html-nocache into develop 2026-09-06 14:05:45 +08:00
Owner

背景

配合 #1732(懒加载 chunk 失效白屏 ErrorBoundary 自动刷新)。排查发现 nginx 缓存策略有两层缺陷,导致 index.html 实际被浏览器长期缓存:

  1. 运行时容器用 bind mount 挂载服务器上的 nginx-staging.conf,而该文件是旧版(无 index.html no-cache 块);
  2. 即使最新仓库配置也有漏洞location = /index.html 只对直接访问 /index.html 生效;用户从 /login/app/dashboard 等路由进入时走 location /try_files $uri /index.html 回退,返回的 HTML 没有 no-cache 头(实测 staging 确认 /login、/index.html 均无 Cache-Control)。旧标签页因此长期缓存含旧 chunk 引用的 HTML,发版后点菜单 → 旧 chunk 404 → 白屏。

修复

location / 块加 add_header Cache-Control "no-cache" always;

  • try_files 回退的 SPA HTML 与 HTML 文档每次校验(ETag/Last-Modified),发版后能拿到新 chunk 引用
  • 带 hash 的静态资源由更高优先级的正则 location ~* \.(js|css...) 匹配,保持 expires 1y + public, immutable,不受影响
  • always 保证 4xx/5xx 也带头

5 份配置同步修改(infra/docker/nginx{,-staging,-production}.conf + deploy/configs/nginx{,-staging,-production}.conf),消除多份配置不一致的隐患。

部署说明

服务器 bind mount 的配置是独立文件(不在镜像里),合并后需把最新 nginx-staging.conf 同步到 /var/lib/xiaoxia-saas-staging/nginx-staging.confnginx -s reload(staging 生效);生产发布时同步 nginx-production.conf。此步由部署流程/前端协助在服务器执行。

验证

部署后实测:

  • /login/app/assets 等路由的 HTML 响应头含 cache-control: no-cache
  • 带 hash 的 /assets/*.js 响应头保持 cache-control: public, immutable(max-age=31536000)
## 背景 配合 #1732(懒加载 chunk 失效白屏 ErrorBoundary 自动刷新)。排查发现 nginx 缓存策略有两层缺陷,导致 index.html 实际被浏览器长期缓存: 1. 运行时容器用 bind mount 挂载服务器上的 nginx-staging.conf,而该文件是旧版(无 index.html no-cache 块); 2. **即使最新仓库配置也有漏洞**:`location = /index.html` 只对直接访问 `/index.html` 生效;用户从 `/login`、`/app/dashboard` 等路由进入时走 `location /` 的 `try_files $uri /index.html` 回退,返回的 HTML **没有 no-cache 头**(实测 staging 确认 /login、/index.html 均无 Cache-Control)。旧标签页因此长期缓存含旧 chunk 引用的 HTML,发版后点菜单 → 旧 chunk 404 → 白屏。 ## 修复 在 `location /` 块加 `add_header Cache-Control "no-cache" always;`: - try_files 回退的 SPA HTML 与 HTML 文档每次校验(ETag/Last-Modified),发版后能拿到新 chunk 引用 - 带 hash 的静态资源由更高优先级的正则 location `~* \.(js|css...)` 匹配,保持 `expires 1y + public, immutable`,不受影响 - `always` 保证 4xx/5xx 也带头 5 份配置同步修改(infra/docker/nginx{,-staging,-production}.conf + deploy/configs/nginx{,-staging,-production}.conf),消除多份配置不一致的隐患。 ## 部署说明 服务器 bind mount 的配置是独立文件(不在镜像里),合并后需把最新 nginx-staging.conf 同步到 `/var/lib/xiaoxia-saas-staging/nginx-staging.conf` 并 `nginx -s reload`(staging 生效);生产发布时同步 nginx-production.conf。此步由部署流程/前端协助在服务器执行。 ## 验证 部署后实测: - `/login`、`/app/assets` 等路由的 HTML 响应头含 `cache-control: no-cache` - 带 hash 的 /assets/*.js 响应头保持 `cache-control: public, immutable`(max-age=31536000)
xiaoxia added 1 commit 2026-09-06 13:56:02 +08:00
fix: SPA 路由回退的 HTML 补 no-cache 头,修发版后旧标签页白屏
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 30s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 32s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m13s
AI Code Review / AI Code Review (pull_request) Successful in 1m33s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m36s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m50s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m17s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m2s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 5m4s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 8m58s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 4s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 6m33s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 8s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 18s
f066a8ef4e
问题排查发现两层缺陷:
1. 运行时 bind mount 的 nginx 配置是服务器旧版(无 index.html no-cache 块);
2. 即使最新仓库配置,location = /index.html 只对直接访问 /index.html 生效,
   用户从 /login、/app/dashboard 等路由进入时走 location / 的 try_files 回退,
   返回的 HTML 没有 no-cache 头——旧标签页会长期缓存含旧 chunk 引用的 HTML,
   发版后点菜单就 Failed to fetch dynamically imported module 白屏。

修复:在 location / 块直接 add_header Cache-Control no-cache:
- try_files 回退的 SPA HTML 与 HTML 文档每次校验,发版后能拿到新 chunk 引用
- 带 hash 的静态资源由更高优先级的 location ~* \.(js|css...) 匹配,
  保持 expires 1y + immutable,不受影响

5 份配置同步:infra/docker/nginx{,-staging,-production}.conf +
deploy/configs/nginx{,-staging,-production}.conf(保持一致,避免再混用)

🚀 预览环境已部署

项目 详情
PR号 #1735
预览链接 https://pr-1735.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1735 | | 预览链接 | [https://pr-1735.preview.xiaoxiajianji.com](https://pr-1735.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
Collaborator

代码审查结果 - PR #1735

⚠️ 问题(1个需要修改)

  1. 所有修改文件 location / 块:Nginx add_header 指令会导致该 location 块不再继承 server 层级的 header。如果 server 块中配置了安全头(如 X-Frame-Options, Strict-Transport-Security, X-Content-Type-Options 等),在 location / 的响应中这些头将会丢失,从而引发安全风险或功能异常。必须检查并在此处补全必要的安全头。

💡 建议(1个可选)

  1. 验证正则 location 优先级:注释提到“带 hash 的静态资源由下方 ~* .(js|css...) location 优先匹配”,请务必确认配置文件中确实存在且位置正确的正则匹配 location 块。如果正则 location 缺失或优先级低于 location /,静态资源将被错误地设置为 no-cache,导致性能下降。

格式检查通过 | 逻辑审查需修改 | 性能无明显问题


🤖 由 AI 代码审查机器人自动生成 | 2026-09-06 05:57:34 | 模型:

## 代码审查结果 - PR #1735 ### ⚠️ 问题(1个需要修改) 1. **所有修改文件 location / 块**:Nginx `add_header` 指令会导致该 location 块不再继承 `server` 层级的 header。如果 `server` 块中配置了安全头(如 `X-Frame-Options`, `Strict-Transport-Security`, `X-Content-Type-Options` 等),在 `location /` 的响应中这些头将会丢失,从而引发安全风险或功能异常。必须检查并在此处补全必要的安全头。 ### 💡 建议(1个可选) 1. **验证正则 location 优先级**:注释提到“带 hash 的静态资源由下方 ~* \.(js|css...) location 优先匹配”,请务必确认配置文件中确实存在且位置正确的正则匹配 location 块。如果正则 location 缺失或优先级低于 `location /`,静态资源将被错误地设置为 `no-cache`,导致性能下降。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ✅ 性能无明显问题 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-09-06 05:57:34 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
auto-approve-bot merged commit 2ce3a5efd3 into develop 2026-09-06 14:05:45 +08:00
auto-approve-bot deleted branch fix/nginx-html-nocache 2026-09-06 14:05:46 +08:00

🗑️ 预览环境已清理

PR #1735 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1735 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.