nginx默认仅启用last-modified(基于文件mtime),需显式配置etag on;才生成弱etag(大小+mtime);客户端同时携带if-none-match和if-modified-since时,nginx优先校验etag,命中即返回304,不再检查时间戳。

想让浏览器更愿意复用本地缓存,关键不是堆砌头信息,而是让 ETag 和 Last-Modified 各司其职、按需启用。Nginx 默认只发 Last-Modified,Etag 需手动打开,且两者不会“自动双重校验”——实际行为完全取决于客户端带什么头、你配了什么指令、资源本身是否稳定。
明确 Nginx 的默认行为和触发逻辑
Nginx 对静态文件自动设置 Last-Modified(基于文件系统 mtime),但默认不生成 ETag。只有显式写 etag on; 才会启用弱 ETag(格式为 W/"size-mtime")。客户端请求时:
- 只带
If-None-Match→ Nginx 仅比对 ETag - 只带
If-Modified-Since→ Nginx 仅比对 Last-Modified - 两个头都带 → 先验 ETag,命中即返回 304,不再查时间戳
判断是否真需要开启 ETag
ETag 不是“缓存增强剂”,它解决的是 Last-Modified 精度不足的特定问题:
- 适合开:内容频繁更新但 mtime 可能不变的场景,比如 Webpack 构建产物(含 hash)、脚本生成的 JSON/JS/CSS、CMS 导出的静态页
- 不必开:普通图片、字体、纯文件托管的 CSS/JS——mtime 稳定可靠,加 ETag 反而增加 inode 查询开销,尤其在大文件或高并发下
配置要点:简洁 + 匹配后端行为
ETag 生效的前提是响应中已有 Last-Modified 头。如果后端 API 没返回该头,Nginx 的 etag on; 不会凭空生成 ETag:
- 检查真实响应:
curl -I https://your-api.com/data,确认含Last-Modified - 若无,需在后端添加(如 Express 用
res.set('Last-Modified', new Date().toUTCString())) - Nginx 配置中只需加
etag on;,放在server或location块内即可 - 避免同时手动设
ETag响应头又开etag on;,可能冲突
搭配 Cache-Control 明确协商意图
光有 ETag 或 Last-Modified 不够,客户端得知道“该去问服务器”。靠 Cache-Control 控制行为:
- 推荐配
public, max-age=0, must-revalidate:强制每次用前验证,启用协商缓存 - 避免
no-cache被误解为“不缓存”,它其实表示“用前必须验证”,语义正确但易混淆 - 禁用
no-store,否则浏览器跳过所有缓存逻辑











