有人整理了一份避坑清单|91网 - 关于缓存设置的说法——我反复确认了两遍。有新情况我会继续补

引言 有人整理了一份避坑清单,关于网站缓存设置的讨论反复出现误解与踩雷。我把常见问题、正确做法和排查步骤整理在下面,并已反复确认实现细节。文章面向网站维护者、前端工程师和运维同学,力求直接可用:配置示例、场景建议、以及快速排查清单。后续若有新情况会继续补充。
一、先讲清几个核心概念(短而清晰)
- Cache-Control:浏览器和中间缓存主要遵循的指令(max-age、no-cache、no-store、public、private、must-revalidate、s-maxage 等)。
- Expires:旧式头部,存在时被 Cache-Control 优先级覆盖。
- ETag / Last-Modified:验证式缓存,服务器可用 304 减少带宽。
- CDN / 代理缓存:CDN 常读 s-maxage、surrogate-key 等,并可能忽略 query 参数或对同一资源做细分。
- Vary:告诉缓存基于哪些请求头区分缓存(例如 Accept-Encoding、User-Agent)。
- Service Worker:浏览器端强力缓存控制器,可实现离线、缓存策略自定义。
二、最常见的误区与坑(直接示例)
- 误区:设置 Cache-Control: no-cache 就完全不缓存 说明:no-cache 意味着每次需要向服务器验证(可能返回 304),并不禁止缓存本地存储。想完全不缓存要用 no-store。
- 误区:用 query string 做版本控制永远可靠 说明:部分 CDN/代理默认不缓存带 query 的请求,或者需要额外配置。更稳妥的是文件指纹(hash)作为文件名。
- 误区:把 HTML 文件缓存设置为长期(比如一年) 说明:HTML 是动态入口,长期缓存会导致用户看到旧页面。若要长期缓存页面,必须配合变更 URL 或强制更新机制。
- 误区:ETag 在多台服务器间天然可用 说明:不同机器生成的 ETag 可能不同,导致缓存命中率下降。可采取一致的 ETag 策略或使用 Last-Modified。
- 误区:压缩和缓存冲突 说明:若未正确使用 Vary: Accept-Encoding,会把压缩和未压缩版本混淆缓存,导致错误响应。
三、按场景给出可直接套用的建议(包含示例头) 1) 静态资源(JS/CSS/图片)
- 思路:可缓存长期 + 文件指纹(文件名含 hash)
- 推荐头: Cache-Control: public, max-age=31536000, immutable
- 说明:immutable 对于无需频繁更新的资源能提升性能;搭配文件指纹可安全长期缓存。
2) HTML 页面(入口文档)
- 思路:短缓存或验证式 + CDN 缓存可短期保存
- 推荐头: Cache-Control: no-cache, must-revalidate 或 Cache-Control: public, max-age=60, s-maxage=60
- 说明:使用 ETag/Last-Modified 帮助 304,或把 HTML 缓存时间设置为几十秒到几分钟并在发布时清理 CDN 缓存。
3) API(需要认证或返回动态用户数据)
- 如果返回个性化敏感数据: Cache-Control: no-store
- 如果是可缓存的公共 API(如价格表、汇率,但更新频繁): Cache-Control: public, max-age=30, s-maxage=60, stale-while-revalidate=30
- 说明:s-maxage 面向代理/CDN,stale-while-revalidate 可提升可用性和延迟表现。
4) 登录后页面 / 带 Cookie 的响应
- 需要避免缓存到共享 CDN: Cache-Control: private, no-store
- 说明:private 允许私有浏览器缓存但禁止共享缓存。
5) 离线或 PWA(Service Worker)
- 策略:HTML 用 network-first;静态资源用 cache-first(带版本策略)
- 说明:Service Worker 能自定义缓存生命周期,但部署时注意版本管理与更新提示。
四、进阶配置与性能提升技法
- s-maxage 与 surrogate-control:用来针对 CDN 单独控制过期时间。
- stale-while-revalidate / stale-if-error:降低延迟并在源站不可用时服务旧内容。
- Surrogate-Key:便于按照标签清理 CDN(Cloudflare Worker、Fastly 等支持)。
- HTTP/2 Push:慎用 —— 若用静态资源指纹和良好缓存,Push 的收益有限且可能浪费带宽。
- 预缓存(prefetch/preload):合理使用可优化关键资源加载,但不要滥用。
五、常用坑位与对应修复方法(快速对照)
- 问题:静态文件更新后用户仍旧加载旧文件 解决:使用文件指纹 + 发布后清理 CDN 缓存或触发资源名称变更。
- 问题:CDN 不缓存带 query 的资源 解决:改用文件名版本或配置 CDN 以缓存指定 query 参数。
- 问题:ETag 导致缓存未命中(多实例) 解决:统一 ETag 生成规则,或使用 Last-Modified。
- 问题:压缩与缓存冲突,返回乱码或错误内容 解决:在服务器加入 Vary: Accept-Encoding,确保 CDN 区分不同编码版本。
- 问题:私有数据被 CDN 缓存 解决:在响应中设置 Cache-Control: private/no-store,或在 CDN 端配置不缓存带 Cookie 的请求。
六、上线前与故障排查的实用检查清单(逐项核对)
- 使用 curl -I URL 查看响应头(Cache-Control, Expires, ETag, Vary)
- 浏览器 DevTools Network:观察 200 / 304、from memory cache / disk cache / service worker
- 在不同节点查看(本地、测试服务器、CDN 边缘)以验证一致性
- 用 WebPageTest / Lighthouse 测试加载性能与缓存行为
- 模拟发布后文件名变更并验证浏览器能拿到新资源
- 检查是否误在敏感响应上设置 public 或长时间缓存
- 记录可复现的场景,便于定位为 CDN 配置问题还是服务器头部设置问题
七、示例:典型 Nginx 配置片段
- 静态资源(长缓存) location ~* .(js|css|png|jpg|jpeg|gif|svg|woff2?)$ { add_header Cache-Control "public, max-age=31536000, immutable"; }
- HTML(短缓存+验证) location / { add_header Cache-Control "no-cache, must-revalidate"; }
- 禁止缓存私密 API location ~* /api/private/ { add_header Cache-Control "no-store, private"; }
八、部署与发布建议(避免现场踩雷)
- 在生产环境推送新版本前先在 staging 验证缓存头与 CDN 行为。
- 发布时优先换文件名(指纹)而不是依赖清理缓存;清理动作作为补充。
- 设定自动化检查:CI 检查静态资源是否带 hash、检查常见 Cache-Control 问题。
- 为重大变更准备回滚计划,并在发布前通知相关团队以便监测异常缓存问题。
九、工具与参考
- curl、httpie(查看头部)
- 浏览器 DevTools(Network)
- WebPageTest / GTmetrix / Lighthouse(性能与缓存测试)
- CDN 文档(Cloudflare、Fastly、Akamai 等)——各家行为细节不同,部署前阅读对应文档非常有帮助
结语(我已复核两遍) 上面把常见的缓存误解、按场景的实用建议、排查清单和示例配置一并列出,很多坑都能通过合理的版本策略与正确的头部设置规避。我反复确认了关键点两遍,若后续遇到新的边缘情况或某家 CDN 发布了改变策略,我会继续补充更新。需要我把你的站点当前响应头帮你检查一遍并给出具体改法吗?