协商缓存严格比对if-none-match与etag或if-modified-since与last-modified的值,而非内容是否变化;etag不变则返回304,即使内容已变,反之亦然;强etag(内容哈希)更可靠,弱etag或last-modified易受时间戳干扰导致误判。

这种情况其实不会触发304,因为协商缓存的判断逻辑不依赖“内容是否变化”,而是严格比对客户端传来的 If-None-Match 或 If-Modified-Since 与服务器当前生成的 ETag 或 Last-Modified 值。
ETag 不变 ≠ 内容没变,但协商缓存只认 ETag 值本身
ETag 是服务器为资源生成的唯一标识,常见实现有两类:
- 强 ETag(如基于文件内容哈希):内容不变 → ETag 不变;内容一改 → ETag 必变。此时若文件仅改了时间戳但内容未动,ETag 不变,服务器比对成功,返回 304。
- 弱 ETag 或基于 mtime 的 ETag(如 Nginx 默认的 inode+mtime):文件系统修改时间(mtime)变了,即使内容完全一样,ETag 也可能变;反过来,如果只是 touch 文件(更新时间戳)而内容没动,且服务器用的是纯 mtime 生成 ETag,那 ETag 就会变 → 比对失败 → 返回 200。
Last-Modified 更容易受时间戳干扰
如果只靠 Last-Modified 做协商缓存:
- 文件被 touch(更新修改时间),Last-Modified 变了 → 浏览器下次带 If-Modified-Since 发起请求 → 服务器发现时间戳已更新 → 返回 200,哪怕内容一字未动。
- 这意味着“文件变化但内容未变”时,协商缓存反而失效,无法返回 304 —— 它不是“聪明地识别内容一致”,而是“老实按头信息比对”。
真正能规避这类问题的做法
想让协商缓存更可靠,关键在服务端如何生成标识符:
- 静态资源(如 HTML、JS、CSS)建议启用内容哈希型 ETag(例如 Nginx 开启
etag on并确保未禁用 inode/mtime 之外的校验,或配合构建工具生成 content-based ETag)。 - 避免用单纯 touch 更新文件;部署时若需刷新缓存,应改内容或加版本号(如
app.js?v=2),而非仅改时间戳。 - 动态资源(如 PHP 页面)必须由后端主动计算并返回 ETag(比如对输出内容做 md5),不能依赖文件系统元数据。
协商缓存不是内容感知系统,它只做字符串/时间戳比对。所谓“聪明”,其实是配置得当的结果,而不是协议自动理解语义。











