缓存并不是每次想知道副本是否最新时就重新下载整个文档。它把验证器发回源站,由源站用小体积的 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 的原因。

来源