HTTP 缓存进阶:Cache-Control、ETag、CDN 与最佳实践
缓存的目标:更快、更省流量、更稳。现代站点通常不只有“浏览器缓存”,还会有 CDN、反向代理等中间层,所以理解缓存要从“链路”出发。
先看图:强缓存 vs 协商缓存的命中路径
一句话记住:
- 强缓存:没过期就直接用本地,不发请求
- 协商缓存:会发条件请求(带指纹/时间),服务器可能返回 304
缓存不止浏览器:还有 CDN(中间层缓存)
常见链路:
- 浏览器(memory/disk)
- CDN/代理(共享缓存)
- 源站(你的服务器)
同一个 URL,可能在不同层命中缓存。你在 DevTools 里看到的 200/304/from disk cache 只是浏览器层的表现;CDN 命中通常要看响应头(例如 Age、X-Cache 等,取决于平台)。
最重要的响应头:Cache-Control(优先级最高)
Cache-Control 决定“能不能缓存、缓存多久、是否需要重新校验”:
max-age=3600:强缓存 1 小时(浏览器层)public/private:是否允许共享缓存(CDN/代理)缓存s-maxage=3600:共享缓存(CDN)使用的强缓存时长(优先于 max-age)no-cache:允许缓存,但每次都要协商(可能 304)no-store:完全不缓存(敏感信息常用)must-revalidate:过期后必须向服务器确认,不能直接用过期副本immutable:资源在 max-age 内绝不会变(通常配合 hash 文件名)
Expires是旧头,表达绝对时间;现在优先用Cache-Control。
协商缓存:ETag 与 Last-Modified
协商缓存有两套“对比方式”:
- 基于内容指纹:
ETag↔If-None-Match - 基于修改时间:
Last-Modified↔If-Modified-Since
通常优先用 ETag(更准确)。如果资源没变:服务器返回 304 Not Modified,浏览器继续用本地文件。
Vary:同一 URL 可能有“多个版本”
Vary 的作用是告诉缓存:同一个 URL 在不同请求头下可能返回不同内容。例如:
Vary: Accept-Encoding:gzip/br 压缩版本不同Vary: Accept-Language:中英文内容不同
如果缺少正确的 Vary,就可能发生“拿到不属于你的缓存版本”的问题(例如语言错乱、内容不匹配)。
三类资源的推荐策略(最实用)
1) 带 hash 的静态资源(JS/CSS/图片)
目标:尽量命中强缓存,更新靠文件名变化。
txt
Cache-Control: public, max-age=31536000, immutable2) HTML(入口页)
目标:更新要及时,避免用户拿到旧 HTML 指向旧资源。
txt
Cache-Control: no-cache(含义:可以缓存,但每次都要协商确认。)
3) API 响应
如果包含用户隐私或登录态相关数据,优先:
txt
Cache-Control: no-store如果是公共、可缓存的数据(例如文章列表),可以考虑短缓存 + 协商,或走 CDN(是否允许取决于业务与权限)。
如何在浏览器里验证(一步步排查)
打开 DevTools -> Network,点开某个请求:
- 看
Status:200(新内容)或304(复用本地) - 看
Size:是否显示from disk cache/from memory cache - 点开请求 -> Headers:查看
Cache-Control、ETag等 - 勾选 “Disable cache” 只影响 DevTools 打开期间(排查缓存问题很有用)
一句话总结
缓存策略最稳的组合通常是:
- 静态资源:文件名带 hash + 长强缓存
- HTML:
no-cache(或短max-age) - 接口:按业务决定是否缓存,敏感数据用
no-store


