协商缓存核心是浏览器“先问再用”,Nginx自动基于文件系统生成Last-Modified和(启用etag on后)弱ETag,响应条件请求返回304;两者分工协作:仅If-Modified-Since时比对时间戳,仅If-None-Match时比对ETag,两者俱全则优先校验ETag。
协商缓存的核心是让浏览器“先问再用”:每次请求前,带着上次拿到的标识去服务端验证是否还能继续用本地副本。Nginx 本身不写业务逻辑,但它能自动基于文件系统信息生成 Last-Modified 和(启用后)ETag,并响应条件请求返回 304 Not Modified —— 整个过程无需前端代码干预,全靠响应头和 Nginx 配置配合。
Last-Modified 是资源最后修改时间(精度为秒),由 Nginx 对静态文件自动设置;ETag 是资源唯一标识(如 W/"12345-67890"),默认不开启,需显式配置 etag on 才会基于文件大小和修改时间生成弱 ETag。
两者不是叠加增强,而是分工协作:
If-Modified-Since → Nginx 只比对 Last-Modified
If-None-Match → Nginx 只比对 ETag
ETag,命中即返回 304,不再查时间戳默认行为已支持基础协商缓存,但要稳定生效,建议明确配置以下几项:
location 块中添加 etag on; —— 启用 ETag 自动生成(尤其适合构建产物含 hash、或内容可能更新但 mtime 不变的场景)if_modified_since exact;(Nginx 默认值),确保时间比对严格精确add_header Cache-Control "public, max-age=0, must-revalidate"; —— 明确告诉浏览器“缓存立即过期,但必须先验证”,从而触发条件请求add_header ETag,否则与 etag on 冲突;也不建议对纯静态资源(如图片、字体)强行加 ETag,徒增 inode 查询开销协商缓存失效往往不是 Nginx 没配好,而是链路中某个环节打断了验证流程:
Last-Modified 头 → Nginx 的 etag on 就不会生效(它依赖该头派生 ETag)Set-Cookie 或 Vary: * → Nginx 默认跳过缓存逻辑,包括协商验证Cache-Control: no-cache 但没配 must-revalidate → 浏览器可能忽略验证意图,行为不一致app.a1b2c3.js)→ 这类资源更适合强缓存(immutable + max-age=31536000),禁用协商缓存反而更高效不用猜,直接用命令验证:
curl -I https://yoursite.com/static/app.js,检查响应头是否含 Last-Modified 和/或 ETag
curl -I -H "If-None-Match: "xxx"" -H "If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT" https://yoursite.com/static/app.js,看是否返回 304 且无 Content-Length
304、Size 显示 from disk cache 或 from memory cache 即表示成功