缓存并不是每次想知道副本是否最新时就重新下载整个文档。它把验证器发回源站,由源站用小体积的 304 Not Modified 或新的响应体来回答。验证是 HTTP 缓存的另一半,而验证器正是让它变得廉价的关键。
验证器是可以比较的最新性答案
验证器是当表示发生变化时也随之变化的元数据。HTTP 定义了两类:
- 强验证器(ETag) 标识字节的特定版本,比较起来可靠。
- 弱验证器(Last-Modified) 是秒级时间戳;同一秒内的两次写入可能被漏掉。
源站至少应发送一个验证器。Caddy 默认同时带 ETag 与 Last-Modified,因此本网站的公共边缘同时返回两者:
etag: "dkx84jwth7nkc9e"
last-modified: Mon, 24 Aug 2026 13:55:23 GMT
条件请求用头部换取可能的 304
客户端用 If-None-Match 提交它已持有的验证器,源站与当前值比较。匹配时,源站不重发响应体就确认了它:
$ curl -s -o /dev/null -w 'HTTP %{http_code}\n' -H 'If-None-Match: dkx84jwth7nkc9e' https://www.xbcatm.com/
HTTP 304
$ # size_download == 0(重新验证命中时)
不匹配或未发验证器时,源站以 200 返回新响应体。304 不是失败:它是用更便宜的方式说缓存副本仍然有效。
强与弱不可互换
用强 ETag 做条件 GET,比较才是权威的。弱验证器只承诺响应体在有意的语义上相近,因此范围请求偏好强验证器。http-range-requests 笔记讲字节选择;这篇讲"我的副本是否仍是答案"。
重新发布时会发生什么
当文件树变化,页面的 ETag 也随之变化,做重新验证的缓存会取到新响应体。如果某次发布更新了字节却未动 Last-Modified,ETag 仍然能捕捉到变化——这正是发布清单记录字节校验和,而非只记 mtime 的原因。
来源
- RFC 9110 — HTTP Semantics,验证器、
ETag与条件请求字段,已核验 2026-06-30。 - RFC 9111 — HTTP Caching,新鲜度、
If-None-Match与304,已核验 2026-06-30。 - MDN: HTTP 条件请求,304 与验证器行为,已核验 2026-06-30。