浏览器明明有缓存,为什么还在发请求:理解Cache-Control与ETag

举报
yd_232671198 发表于 2026/10/31 08:57:57 2026/10/31
【摘要】 打开开发者工具时,经常会看到一种现象:图片没有变化,浏览器却又发了一次请求。有人因此判断“缓存没有生效”,随后把缓存时间调得很长。结果新版本发布后,用户又迟迟看不到更新。这里混在一起的,其实是两个问题:浏览器现在能不能直接使用已有内容,以及它是否需要向服务器确认内容有没有变化。理解这两道判断,才能把缓存配置放在合适的位置。服务器第一次返回资源时,可以附带这样的响应头:HTTP/1.1 200...

打开开发者工具时,经常会看到一种现象:图片没有变化,浏览器却又发了一次请求。有人因此判断“缓存没有生效”,随后把缓存时间调得很长。结果新版本发布后,用户又迟迟看不到更新。

这里混在一起的,其实是两个问题:浏览器现在能不能直接使用已有内容,以及它是否需要向服务器确认内容有没有变化。理解这两道判断,才能把缓存配置放在合适的位置。

服务器第一次返回资源时,可以附带这样的响应头:

HTTP/1.1 200 OK
Cache-Control: public, max-age=600
ETag: "asset-v7"
Content-Type: text/css

max-age=600 表示资源的缓存新鲜期为六百秒。浏览器会结合响应中的日期、年龄等信息计算当前年龄;刚取得且年龄接近零的响应,在这段时间内通常可以直接复用。此时网络面板可能显示从内存或磁盘缓存读取,服务器也就不必再次传输文件。

超过新鲜期,并不代表文件一定发生了变化。浏览器保存了 ETag,就可以发送条件请求,让服务器判断它持有的版本是否仍然有效:

GET /assets/app.css HTTP/1.1
If-None-Match: "asset-v7"

如果版本一致,服务器可以返回 304 Not Modified。这次响应不包含资源正文,浏览器继续使用本地内容。因此,看到请求并不能直接认定缓存失效:协商缓存省下的是正文传输,仍然需要一次网络往返。

另一个常见误解来自 no-cache。它允许保存响应,但通常要求复用前先向服务器验证。它适合希望及时确认更新、同时还想保留协商能力的资源。真正表达“不要存储”的指令是 no-store。对带有用户私密数据的响应,还应根据业务设置 private 或更严格的存储策略,避免让共享缓存复用个人内容。

实际项目可以先按资源的更新方式划分。带内容指纹的静态文件,例如 app.a81f9c.js,内容改变时文件名也改变,适合较长的新鲜期,并配合 immutable。入口 HTML 往往需要尽快取得新版本,可以使用 no-cache 并配置有效的 ETag。这套安排成立的前提是发布过程中保留必要的旧静态资源,否则仍在访问旧页面的用户可能遇到文件不存在。

不要简单地给所有接口都设置 public。响应如果受登录身份、语言或请求头影响,缓存必须正确区分不同版本。某些差异可以通过 Vary 表达,但它不能替代对个人信息的缓存边界设计。CDN、网关和浏览器也可能有各自的规则,排查时要沿着实际请求链检查。

ETag 也需要代表真实的内容版本。如果所有响应都使用一个固定值,服务器可能错误地告诉客户端“没有更新”。如果响应经过压缩或其他变换,生成和比较标识时还需要遵循服务端框架对不同表示的处理方式。优先使用框架已经实现的机制,避免随意拼一个时间戳冒充版本管理。

验证配置时,可以临时把新鲜期设成十秒,关闭开发者工具里的“禁用缓存”,连续访问同一资源。先观察新鲜期内是否复用缓存,再等待过期,检查请求是否携带条件头、服务器是否返回 304。最后修改资源内容,确认客户端能收到新的正文和版本标识。

强制刷新会改变浏览器的缓存行为,不能只用这一种操作判断正常访问是否正确。把“直接复用”“先验证再复用”“重新下载”三种情况分别观察清楚,缓存问题通常就会从一组看似神秘的状态码,变成可以逐步验证的配置问题。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。