HTTP 缓存验证实战:用 ETag 与 304 区分“没更新”和“没请求”

10-01 6阅读

网页刷新后没有变化,既可能是内容没更新,也可能是浏览器仍在使用缓存。排查时先拆开两个问题:这次请求有没有发出去;服务器确认的资源版本是否改变。本文适用于普通 GET 页面或静态文件的缓存检查,示例域名和版本标识需要替换,不应直接套用到登录态接口。

HTTP 缓存验证实战:用 ETag 与 304 区分“没更新”和“没请求”

AI生成概念配图:浏览器与服务器通过版本标识确认缓存是否仍可使用,仅辅助理解,不代表真实界面或实测结果。

先给响应头分工

Cache-Control 主要表达缓存与复用策略;ETag 是服务器为所选表示提供的版本验证器;Last-Modified 则表达修改时间。它们并不互相替代。no-cache 允许保存响应,但复用前需要成功验证;no-store 要求缓存不要存储该次请求和响应。把两者都翻译成“禁用缓存”,很容易让配置目标与实际行为脱节。

下面是一组用于说明的响应头。max-age=60 表示示例中的新鲜度期限,不是所有网站都应使用的推荐值。ETag 的引号属于语法,不要在复制时去掉。

HTTP/1.1 200 OK
Cache-Control: public, max-age=60
ETag: "article-v3"
Content-Type: text/html; charset=utf-8

用条件 GET 重现一次验证

curl -sS -D first.headers -o first.html \
  https://example.com/article.html

# 将下面的标识替换为 first.headers 中实际的 ETag
curl -sS -D checked.headers -o checked.body \
  -H 'If-None-Match: "article-v3"' \
  https://example.com/article.html

若资源匹配该验证器,GET 的正常验证结果可以是 304。它告诉接收方复用已有表示,并不携带新的页面正文。若表示已经变化,通常会收到带新正文的 200。curl 默认不会像浏览器一样自动维护页面缓存,以上是在手动构造条件请求;本地已有 first.html,才能把“复用”这件事解释完整。

再做一次真正的更新测试

在测试环境修改资源,重新执行条件请求,检查响应状态、新 ETag 与正文是否一起变化。如果只改了模板却仍返回旧标识,要追查标识的生成逻辑和发布流程;如果源站正确而 CDN 仍旧,则对照两层响应头与刷新规则。不要只用首页截图判断是否发布成功,因为页面还可能引用旧版本脚本或图片。

当服务同时提供 ETag 和 Last-Modified 时,不要自行把两个条件拼成“任意一个没变就返回 304”。服务器需要遵循 HTTP 条件请求规则;If-None-Match 存在时,If-Modified-Since 不应取代它。弱 ETag 带 W/ 前缀,适合语义层面的验证,不能随意当作字节完全一致的证明。

最容易遗漏的是缓存对象边界

同一路径可能因语言、压缩或登录状态返回不同内容。需要按相关请求头区分的响应,应正确处理 Vary;带个人信息的页面还要检查 private 或 no-store 等策略。这里的工程判断是:先写清“哪些访问者能共享同一份内容”,再选择缓存时长,比先抄一串缓存头更可靠。

验收时分别记录首次访问、未修改时验证、修改后验证三个结果,并保留响应头。304 减少的是重新发送表示正文的需要,不代表零网络请求,也不保证整个页面一定更快。最终仍应在浏览器网络面板中确认资源来源、请求链路与真实加载行为。

参考资料

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。