前端静态资源缓存与部署实践总结
1. 背景
在与运维沟通时,我们发现前端部署方式的不同,会带来缓存与版本更新上的差异。以前我们通过 bucket 静态托管,现在改成了 k8s Pod + Nginx,这中间存在一些需要特别注意的问题。
2. Bucket 部署 vs k8s 部署
-
Bucket 静态托管(增量更新)
- 构建产物上传到对象存储(S3/OSS/GCS)。
- 采用增量更新:只上传新生成的 hash 文件,旧文件保留。
- 用户旧缓存对应的文件依然存在,不会 404。
- 上线体验平滑。
-
k8s 部署(镜像全量更新)
- 前端文件打包到镜像中,Pod 滚动更新时全量替换。
- 旧 Pod 被销毁,旧版本文件随之消失。
- 在 Pod 切换过程中,用户可能请求到旧 Pod 的
index.html
→ 加载旧的 JS 文件。
3. 为什么客户端会请求到旧 JS 文件?
主要有以下几类原因:
-
浏览器缓存
- 如果文件名没带 hash,浏览器会用缓存的旧 JS。
- 即使带 hash,如果
index.html
缓存了,仍然会引用旧的 hash 文件。
-
Nginx Header 设置不完整
- 默认
add_header
不会在 304 响应时生效,需要加always
。
- 默认
-
k8s 滚动更新
- 在新旧 Pod 共存的窗口期,用户可能请求到旧 Pod 的
index.html
。
- 在新旧 Pod 共存的窗口期,用户可能请求到旧 Pod 的
-
CDN/代理缓存
- 如果前面挂了 CDN,可能拦截并返回旧文件。
4. 最佳缓存策略
核心思路:
- 入口文件 index.html:禁止缓存
- 带 hash 的静态资源(JS/CSS/图片等):长缓存
为什么?
index.html
体积小,每次都请求不会影响性能,却能保证用户拿到最新版本。- 静态资源带 hash,内容唯一 → 可以放心给长缓存,提高加载性能。
5. 改进版 Nginx 配置
server {listen 80 default_server;root /usr/share/nginx/html;# 默认禁止缓存add_header Cache-Control "no-cache, no-store, must-revalidate" always;add_header Pragma "no-cache" always;add_header Expires 0 always;# index.html 禁止缓存location = /index.html {add_header Cache-Control "no-cache, no-store, must-revalidate" always;add_header Pragma "no-cache" always;add_header Expires 0 always;}location = /v2/index.html {add_header Cache-Control "no-cache, no-store, must-revalidate" always;add_header Pragma "no-cache" always;add_header Expires 0 always;}# 带 hash 的静态资源 → 长缓存location ~* \.(?:js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot)$ {add_header Cache-Control "public, max-age=31536000, immutable" always;try_files $uri =404;}# SPA 路由支持location / {index index.html;try_files $uri $uri/ /index.html;}location /v2 {index index.html;try_files $uri $uri/ /index.html;}# 前端版本信息文件location /version {index wati-fe-version.json;alias /usr/share/nginx/html/;add_header Cache-Control "no-cache, no-store, must-revalidate" always;}error_page 500 502 503 504 /50x.html;location = /50x.html { root /usr/share/nginx/html; }error_page 404 403 /40x.html;location = /40x.html { root /usr/share/nginx/html; }
}
6. 总结
- index.html 禁止缓存:保证用户总能获取最新入口文件。
- 静态资源长缓存:充分利用 hash 文件名,提升性能。
- 加上
always
:避免 304 时丢失缓存控制头。 - 注意滚动更新窗口期:可能存在新旧 Pod 并行,带来旧资源问题。
这样配置后,前端项目能既保证 快速更新,又能 充分利用缓存性能。