HTTP 缓存 Cache-Control 与 ETag 详解
时间:2026-11-15 07:22 作者:emer 分类: 无
HTTP 缓存 Cache-Control 与 ETag 详解
一、为什么要做 HTTP 缓存
同一个静态资源(JS、CSS、图片)反复下载既耗带宽又拖慢首屏。HTTP 缓存让浏览器或 CDN 把资源存一份,再次访问直接复用,只在必要时才向服务器确认。缓存分两类:强缓存(不发请求)和协商缓存(发一次轻量请求确认)。
二、强缓存:Cache-Control
服务器通过响应头告诉浏览器资源能存多久,命中期间浏览器根本不发请求,直接用本地副本。
Cache-Control: max-age=86400, public
常见指令:
max-age=秒:缓存有效期,在期内直接用本地;public:允许 CDN、代理缓存;private:只允许浏览器缓存,代理不存;no-cache:可以缓存,但每次都要协商确认;no-store:完全不缓存。
location ~* \.(css|js|png|jpg)$ {
add_header Cache-Control "max-age=604800, public";
}
注意:no-cache 不是“不缓存”,而是“缓存但要协商”;真正不缓存是 no-store。
三、协商缓存:ETag 与 Last-Modified
强缓存过期后,浏览器带上标识问服务器“资源变了吗”,没变就返回 304 Not Modified,不回传正文。
ETag(实体指纹):服务器给资源生成唯一校验值,内容一变 ETag 就变。
# 响应
ETag: "a1b2c3-1234"
# 再次请求带 If-None-Match
If-None-Match: "a1b2c3-1234"
# 未变化返回 304
304 Not Modified
Last-Modified:按修改时间判断,配合请求头 If-Modified-Since。
四、ETag 与 Last-Modified 的取舍
- ETag 按内容指纹,精确;Last-Modified 只到秒级,同一秒多次改识别不出;
- 两者同时存在时,ETag 优先级更高;
- 动态内容、频繁变化用 ETag;纯静态文件也可只依赖时间;
- 分布式多机生成 ETag 要保证算法一致,否则反复 304 失败。
五、缓存更新策略
静态文件带内容指纹文件名是最佳实践:
<script src="/app.8f3a21.js"></script>
内容变了文件名哈希就变,旧文件名长期缓存也不会命中旧版本,HTML 本身则短缓存或不缓存。
六、常见误区
- 把
no-cache理解成不缓存,导致每次全量下载; - 给经常改的入口 HTML 设超长 max-age,用户永远拿旧页面;
- 强缓存超长又不做指纹文件名,更新后用户看不到新版本;
- ETag 每次随机生成,协商缓存彻底失效;
- 只配强缓存不配协商缓存,max-age 一过全部重新下载。
七、验证方法
浏览器 F12 Network 看资源:
- 强缓存命中显示
(disk cache),状态 200,无请求; - 协商缓存命中返回 304;
- 响应头里能看到 Cache-Control、ETag。
curl -I https://example.com/app.js查看返回的缓存头。
八、总结
Cache-Control 强缓存决定“存多久不用问”,ETag/Last-Modified 协商缓存决定“过期后轻量确认是否变化”。静态资源用指纹文件名加长缓存,HTML 短缓存,动态资源用协商。理清强缓存与协商缓存的配合,才能既省带宽又保证更新及时。
标签: 缓存 Cache-Control HTTP ETag