HTTP 缓存机制速查

5 个速查板块 · 关联 4 款站内工具 · 数据最后核对 2026-10-05

强缓存与协商缓存的分工、Cache-Control 每个指令到底说什么、ETag 与 Last-Modified 怎么配合,以及 no-cache 和 no-store 的关键差别。

缓存的作用是让浏览器或少走一次网络,或多省一次传输。展开说有两层:一层是浏览器直接拿本地副本、连请求都不发(强缓存);另一层是发个请求去问服务器「我这份还能用吗」(协商缓存)。

理解这两层之后,「为什么改了代码用户还是看到旧页面」和「为什么每次刷新都重新下载」这两个看似矛盾的问题就都能解释了——它们是同一套机制在不同配置下的结果。

1两步判定:强缓存在前,协商缓存在后

浏览器要一个资源时,按固定顺序判断,只有前一步不通过才会走到下一步。

阶段触发条件是否发出请求服务器返回
强缓存命中本地副本仍在 max-age 有效期内不发请求无(直接读本地)
强缓存过期超出有效期或设为必须校验发出请求(带校验头)由服务器决定
协商缓存命中校验信息表明内容未变发了请求304 Not Modified,不含正文
协商缓存未命中内容已变更发了请求200 OK 加新的完整正文
无任何缓存头响应里没有缓存指令每次发请求由服务器按启发式规则处理

`304` 虽然也走了一次网络往返,但省掉了正文传输。对体积大的文件(图片、字体)收益明显;对很小的接口响应,节省有限,反而多一次往返。

2Cache-Control 指令对照

这是控制缓存行为的核心头部,可以组合多个指令。理解每个指令「否定的是哪一步」,比死记名字更管用。

指令含义实际效果
max-age=3600响应在 3600 秒内可直接复用强缓存的有效期,从响应生成时刻算起
s-maxage=600同上,但只约束共享缓存(CDN、代理)可让 CDN 缓存更短,浏览器缓存更长
no-cache可以存,但每次使用前必须向服务器校验**跳过强缓存,走协商缓存**(并未禁止缓存)
no-store完全禁止存储任何副本既不存也不用,最彻底的禁用,适合含敏感信息的响应
private只允许浏览器这类私有缓存保存阻止 CDN 与代理缓存,常用于带用户身份的响应
public允许任何缓存保存即使带 Authorization 头也可被 CDN 缓存
must-revalidate过期后必须先校验,不得使用陈旧副本断网时不会拿过期内容凑合,而是报错
immutable在有效期内明确不会变化用户刷新页面时也不再发校验请求,适合带指纹的资源
stale-while-revalidate=60过期后 60 秒内可先用旧副本后台同时异步更新,换来更快的首屏

`no-cache` 名字最有误导性:它并不表示「不缓存」,而是「使用前必须校验」。真正想禁止缓存要用 `no-store`。这两个词搞反是排查缓存问题时最浪费时间的一类误解。

3校验型头部:ETag 与 Last-Modified

协商缓存靠一组「请求头带校验值、响应头给出校验值」的头部工作。两者各有局限,同时提供时服务器可自行选择依据。

响应头(服务器给出)请求头(浏览器回传)依据精度
ETagIf-None-Match内容指纹(如哈希)精确到内容,内容没变值就不变
Last-ModifiedIf-Modified-Since最后修改时间精确到秒,秒内多次修改会漏判
ETag 弱校验 W/ 前缀If-None-Match语义等价即可允许内容有细微差异仍算未变
两者同时存在浏览器同时带上服务器优先用 ETagETag 不一致则返回 200 加新内容
CDN 场景Vary 头参与缓存键按 Vary 指定的头区分副本如 Vary: Accept-Encoding 区分压缩与未压缩版

多台服务器做负载均衡时,如果 `ETag` 由各自机器单独生成(例如带 inode 的实现),同一份资源在不同机器上会得到不同 `ETag`,导致协商缓存频繁失效。这类场景应改用基于内容哈希的 `ETag`,或干脆关掉 `ETag` 只留 `Last-Modified`。

4一套常见的缓存策略

「HTML 不缓存、静态资源长缓存」是最主流的组合。核心思路是让变化的入口始终新鲜,让不变的资源尽可能待在本地。

HTML 入口
设 Cache-Control: no-cache(或很短 max-age),每次校验。入口文件很小,校验成本低,保证了发版后用户能立刻拿到新版本
带指纹的静态资源
文件名里含内容哈希(如 app.9f3c2a.js),设 max-age 为一年并加 immutable。内容变了文件名就变,天然规避缓存
不带指纹的资源
设较短 max-age 并配合 ETag 校验,用时间换正确性
接口响应
按数据时效性决定:几乎不变的字典数据可缓存,实时数据用 no-store
CDN 策略
静态资源交给 CDN 长缓存,HTML 走回源校验,s-maxage 控制 CDN 侧的有效期
发版注意
只要入口 HTML 不放长缓存,用户下次访问就会拿到引用新指纹资源的页面,不需要额外清缓存操作

5四个高频误区

改了代码但用户看到旧版
通常是入口 HTML 被缓存了。检查 HTML 的响应头里是否有长 max-age,把它改成 no-cache 或很短的 max-age
每次访问都重新下载
响应里缺少缓存头,或设成了 no-store。确认静态资源是否配了 max-age 与 ETag
刷新和直接访问行为不同
浏览器刷新(F5)会自动给主文档带上 no-cache 语义去校验,Ctrl+F5 则连子资源也强制跳过缓存。所以「刷新后正常」不代表普通用户也正常
关掉缓存就万事大吉
把全部资源设成 no-store 会显著拖慢重复访问,且增加服务器与带宽压力。正确做法是分层:入口校验、资源长缓存

关于「HTTP 缓存机制速查」的常见问题

no-cache 和 no-store 有什么区别?

这是缓存里最容易混淆的一对。no-cache 的意思是「可以存副本,但每次使用前必须先向服务器校验」,所以它跳过的只是强缓存,仍然可能返回 304 省下正文传输;no-store 则是「完全禁止保存任何副本」,浏览器不存、也不用,每次都要完整重新下载。想把一份含敏感信息的响应彻底排除在缓存之外,要用 no-store;想让页面每次都用最新版本但保留校验带来的带宽节省,用 no-cache 就够。

强缓存和协商缓存有什么区别?

强缓存命中时浏览器连请求都不发,直接用本地副本,速度最快,代价是在有效期内无法感知服务器上的更新。协商缓存则是浏览器发出请求并带上校验值(If-None-Match 或 If-Modified-Since),服务器比对后如果内容没变就返回 304,省掉正文传输但仍有一次网络往返。所以选择依据是「这个资源多久会变」:长期不变的用强缓存并配长有效期,可能随时变的用协商缓存保证及时性。实践中两者往往配合使用。

为什么修改了网页,用户看到的还是旧版本?

九成是入口 HTML 被强缓存了。资源文件通常带内容指纹(文件名里有哈希),改了就换名字不会冲突;但 HTML 的文件名固定,一旦被浏览器或 CDN 长时间缓存,用户就一直在用旧页面。解决方式是给 HTML 设置 no-cache 或很短的 max-age,让它每次都走校验;同时确保静态资源文件名带指纹。这样发版后用户下一次访问就能自动拿到新版本,不需要提示他们清缓存。

ETag 和 Last-Modified 该用哪个?

两者都用于协商缓存,最好同时提供,服务器可自行选择。Last-Modified 基于最后修改时间,实现简单,但精度只到秒,同一秒内的多次修改可能漏判,而且文件被重新生成内容没变时时间也会变,导致不必要的下载。ETag 基于内容生成的指纹,只要内容没变值就不变,判断更准确。需要注意的是在多台服务器做负载均衡时,如果 ETag 由各机器独立生成,同一资源可能得到不同值,反而频繁失效,这时应改用内容哈希生成 ETag 或只保留 Last-Modified。

内容标识:本页内容由人工智能辅助生成,并经人工核对与编辑。详见 人工智能生成内容标识说明。
内容说明:本页速查数据由本站自行整理,事实性内容以对应官方规范与文档为准,未复制第三方速查表或百科的表述。数据最后核对日期:2026-10-05。若发现错误或已更新的标准,欢迎通过页脚反馈入口告知。