有人整理了一份避坑清单|91官网:关于缓存设置的说法,我试了三种方法才搞明白…?我不下结论,但信号很明显

缓存听起来是个小细节,拖慢或加快体验只在一瞬间。为了弄清楚“到底怎么设置才不会吃坑”,我在91官网上连续试了三种常见做法:默认托管缓存、基于头信息的分层缓存 + 资源指纹化、以及基于 Service Worker 的客户端缓存。下面把过程、关键观察和一份实操避坑清单整理出来,便于直接在网站上复用。
先说结论性的“信号”(不是绝对结论,只是多次实验给出的明显指向)
- 静态资源使用长期缓存(max-age 很大)+文件指纹(hash)时,页面重复访问速度和缓存命中率最高,且部署更新时最稳妥。
- HTML 页面不宜长期缓存,使用短缓存或 no-cache + 资源指纹化的静态文件组合,既能保证用户能及时看到更新,又能兼顾性能。
- Service Worker 若没有做好版本/更新策略,会导致用户长期看到旧内容,带来的麻烦往往大于收益,除非有专门的维护流程。
- CDN 的“Cache Everything”或默认策略在不同用户/设备上表现不一致,必须在部署策略里有明确的清理和回滚方式。
三种方法的做法与观察(直接上干货)
方法一:依赖托管/平台默认缓存(最省事)
- 做法:不主动设置 Cache-Control/Expires;依赖托管服务(如某些托管平台、共享主机或CDN默认)返回的头。
- 观察:初期看似正常,但在发布新版本或修复BUG时,用户端会出现“部分用户旧资源、部分用户新资源”的情况;不同地区或ISP的缓存策略差异明显;无法保证每次部署都一致。
- 优点:无操作成本,适合测试站或极低频更新的页面。
- 缺点:不可控、难以调试、更新时体验不一致。
方法二:基于 HTTP 头的分层策略 + 资源指纹(推荐主流做法)
- 做法:
- 所有静态资源(JS/CSS/图片/字体)在文件名里带 hash(如 app.abc123.js),并设置 Cache-Control: public, max-age=31536000, immutable。
- HTML 页面设置短缓存或 no-cache(例如 Cache-Control: public, max-age=0, must-revalidate 或 Cache-Control: no-cache),并在部署时更新 HTML 引入的资源指向。
- CDN 配置为尊重源站 Cache-Control,必要时启用自动或按需清理。
- 观察:页面首次加载稍慢(取决于资源大小),重复访问命中率高,部署新版本后用户几乎可以立刻获得新 HTML(因短缓存),而静态资源按需更新(因指纹变化)。
- 优点:清晰、易于自动化集成(CI/CD),兼容大多数浏览器和CDN。
- 小提示(示例 Header):
- 静态资源:Cache-Control: public, max-age=31536000, immutable
- HTML:Cache-Control: public, max-age=0, must-revalidate
- 其它可选:启用 ETag/Last-Modified 做协商缓存,但不要把它当成替代版本化文件名的方案。
方法三:Service Worker(客户端缓存与离线)——高收益但高风险
- 做法:用 Workbox 或手写 SW 做 precache + runtime cache,控制离线体验与快速加载。
- 观察:在做好版本管理(每次发布同时更新 SW 的版本号或 precache 清单)时,性能和离线体验出色。但若发布流程没把 SW 更新和资源替换同步做好,用户会长期看到旧页面或旧脚本,调试成本高。部分浏览器在 SW 行为上也有差异,需要兼容性处理。
- 优点:极佳的离线支持和性能控制。
- 缺点:复杂、易出错、调试麻烦,需明确更新策略与回滚机制。
实操避坑清单(可以直接拿去用)
- 分类设策略:把内容分成三类:HTML(页面)、静态资产(JS/CSS/图片/字体)和动态接口。为每类订好默认策略,不要混合对待。
- 静态资源用指纹化(文件名哈希):任何长期缓存的文件务必在文件名里带版本号/哈希。这样可以把长期缓存与更新解耦。
- HTML 使用短缓存或 no-cache:让浏览器每次都确认是否有新版本(304 验证或重新获取)。
- CDN 尊重源站 Cache-Control:确保 CDN 配置不要无脑覆盖 origin 的 header,或者在 CDN 层面明确规则(例如对 hashed asset 用长缓存、对 HTML 强制短缓存)。
- 部署时自动化清理或版本化:如果不使用指纹化,必须在部署后自动清除 CDN 缓存。最好把清理/失效纳入 CI 流程。
- Service Worker 要有明确更新策略:每次发布都改变 SW 的版本号或预缓存清单;测试回退流程;提供“立刻更新”控制(例如在新 SW 激活时提示用户刷新)。
- 监测与回放:部署后用工具检测不同网络、不同地区的缓存表现(curl -I、Lighthouse、WebPageTest)。
- 测试方法(常用命令):
- 查看响应头:curl -I https://your.site/path
- 强制重新验证:curl -H "Cache-Control: max-age=0" -I https://…
- 浏览器:DevTools Network 面板,勾选 Disable cache(检测首次加载与重复加载差异)。
- 避免把 ETag 当成发布版本控制:ETag 有时在代理/负载均衡器层面行为不一致,最好把 ETag 作为协商缓存的补充,而不是发布流程的唯一手段。
常见坑与如何立刻应对
- 坑:用户反馈“我看不到新样式/脚本”。
立即应对:检查 HTML 的 Cache-Control;确认静态资源是否带 hash;使用 CDN 的清理 API 触发失效;或者让用户做一次硬刷新(Ctrl+F5)。 - 坑:CDN 对 origin header 覆盖不一致。
立即应对:审查 CDN 配置并设置规则(hash 资源长期缓存,HTML 短缓存);如果不确定,开启临时的 CDN development mode 并观察。 - 坑:Service Worker 导致旧页面长期存在。
立即应对:发布新 SW 强制清空旧缓存并监测激活结果;在页面里提供“检测更新并刷新”的按钮作为备用。
简短示例(Nginx 常见配置片段)
- 静态资源(哈希文件): add_header Cache-Control "public, max-age=31536000, immutable";
- HTML(页面): add_header Cache-Control "public, max-age=0, must-revalidate";
收尾(行动版) 1) 如果现在没有版本化静态资源:先把构建系统改成输出带 hash 的文件名。 2) 把 HTML 的 Cache-Control 改成短缓存(max-age=0 或 no-cache)并验证。 3) 把 CDN 设置为尊重 origin header,并在部署流程里加入清理或回滚步骤。 4) 如果要引入 Service Worker,先在测试环境充分验证更新机制,再上线。
不扯理论,直接做这四步能把多数“缓存带来的坑”堵住。需要的话我可以根据你的托管环境(Nginx/Apache/Cloudflare/Vercel/Netlify)写出更具体的配置和部署脚本。想从哪里开始?