缓存配置的第一个问题不是 TTL 设多长,而是这份响应能不能被另一个用户安全地复用。若这个边界不清楚,CDN 的命中率再高,也可能把用户数据或旧版本送到不该看到的人手里。
本文讨论 HTTP 缓存与托管缓存的决策方法,不假设某家 CDN 的默认缓存键、清理速度或地区表现。复核日期为 2026-09-03;供应商的控制台规则应以其当前文档和实际响应为准。
先给响应分类
| 响应 | 首先确认 | 常见方向 |
|---|---|---|
| 带内容哈希的 JS、CSS、图片 | URL 是否会在内容变化时更新 | 可考虑较长新鲜期;新内容必须使用新 URL。 |
| HTML 入口 | 是否需要及时发现新的资源 URL | 允许存储,但复用前要求验证通常更容易更新。 |
| 登录后的页面或个人 API 数据 | 是否含用户、租户或权限相关内容 | 不放入共享缓存;按需求使用私有缓存或不存储。 |
| 公共、变化频繁的接口 | 旧结果能否被接受、如何验证 | 明确验证策略和数据边界,不只套一个通用 TTL。 |
Cache-Control 的 private、public、no-cache 与 no-store 不是同义词。特别是 no-cache 允许存储,但要求复用前验证;no-store 才是阻止存储的指令。MDN 的 HTTP 缓存说明给出了这些差异和适用情形。
版本化比清理更可预测
当静态文件的 URL 包含内容版本或哈希,内容改变时生成新的 URL,旧缓存就不会被当成新内容使用。这种模式使长缓存期成为可能,但前提是构建产物、HTML 引用和部署过程都能保证 URL 与内容同步更新。
不要把“永久缓存”理解为一个可以直接复制的头部。先在预发布环境检查三件事:HTML 是否引用了新的文件名、旧 URL 是否仍只返回旧内容、回滚版本是否仍可获得其依赖的资源。缓存清理可用于处理例外,但它不能清除已经存在于用户浏览器或不受你控制的中间缓存中的内容。
缓存键是数据隔离的一部分
同一路径的响应是否相同,取决于路径之外的输入:查询参数、Cookie、请求头、语言、设备能力都可能影响结果。把这些输入随意忽略,可能让不同用户拿到同一份本不该共享的响应;把所有输入都纳入键,又会让缓存碎片化。
为每个可缓存路由写下“内容由什么决定”。例如,营销页可能只由路径和语言决定;/api/me 明确由身份决定,应不进入共享缓存;带 utm_* 的公共页面如果内容不变,可以在验证后由托管缓存规则规范化。不同 CDN 对查询参数、Cookie 和自定义头的处理不同,部署后应从响应头、访问日志和隔离账号实际验证,而不根据产品名推断。
排查旧内容时沿着请求走
- 记录发生问题的完整 URL、时间、账号状态、地区和用户看到的版本;
- 检查 HTML 是否仍指向旧的版本化资源;
- 读取响应的
Cache-Control、Age、ETag、Last-Modified和缓存服务诊断头; - 排除 Service Worker、浏览器缓存和应用内数据缓存;
- 只对已确认的缓存层采取清理、改键或改头操作,并在同一条件下复测。
ETag 和 Last-Modified 可参与条件请求,但它们不自动证明不同缓存层的一致性。先确定谁生成这些字段,以及响应是否会因认证、Cookie 或查询参数而变化。
发布前检查
- 每个缓存规则都有响应类型、可共享范围和失效方式。
- 私有或按权限变化的内容不会进入共享缓存。
- 版本化资源的 URL 与构建内容绑定,HTML 更新可被验证。
- 缓存键的路径、查询参数、Cookie 和请求头选择有书面理由。
- 出现旧内容时,团队知道需要收集哪些响应头和版本信息。
缓存是可复用响应的协议,不是“让网站变快”的开关。把共享边界写清楚,才能在性能、更新和数据隔离之间做可解释的取舍。
参考资料
- MDN:HTTP 缓存(复核于 2026-09-03)
- MDN:Cache-Control(复核于 2026-09-03)
