Nginx默认基于文件mtime自动设置Last-Modified头,浏览器在缓存有效且响应含该头时自动发送If-Modified-Since请求,Nginx内置比对mtime并返回304,无需手动处理;搭配Cache-Control分层控制强缓存与协商缓存才完整。

要让 Nginx 正确支持协商缓存,关键不是堆砌配置,而是理清 Last-Modified 与 If-Modified-Since 的配合逻辑——前者是服务器告诉浏览器“我这个文件啥时候改的”,后者是浏览器拿着这个时间去问“自那以后你动过它没?”。
Last-Modified 是怎么生成的
Nginx 对静态文件(如 .js、.css、png)默认就基于文件系统修改时间(mtime)自动设置 Last-Modified 响应头,无需额外指令。前提是:
- 资源由 Nginx 直接提供(非 proxy_pass 到后端)
- 没在配置中用 add_header Last-Modified "" 或 unset $sent_http_last_modified 主动清除该头
- Nginx 进程有权限读取文件元数据(否则 mtime 获取失败,头可能为空)
If-Modified-Since 请求是怎么触发的
浏览器只在满足两个条件时才会自动带上 If-Modified-Since:
- 本地已缓存该资源,且缓存未被强缓存策略(如 Cache-Control: max-age=3600)完全锁定
- 响应中曾包含有效的 Last-Modified 头(格式为 RFC 1123 时间,如 Wed, 21 Oct 2022 07:28:00 GMT)
此时浏览器会把上次收到的 Last-Modified 值原样填入 If-Modified-Since 发起请求。Nginx 收到后,比对当前文件 mtime —— 若未更新,直接返回 304;否则返回 200 + 新内容。
Nginx 不需要“手动处理”这个头
很多人误以为要写 if ($http_if_modified_since) { ... } 才能启用协商缓存,其实不必。Nginx 内置行为就是:
- 只要响应里有 Last-Modified,且请求带 If-Modified-Since,就会自动比对 mtime 并决定是否返回 304
- 不需要显式开启模块或添加 rewrite/if 指令
- 真正要检查的是:有没有无意中禁用或覆盖了 Last-Modified
搭配 Cache-Control 才算完整
光靠 304 不够高效,因为每次都要发一次请求。合理做法是分层控制:
- 对不变资源(如带 hash 的 JS/CSS):用 expires 1y; 或 add_header Cache-Control "public, immutable";,彻底避免验证
- 对可能更新的资源(如首页 HTML):设短强缓存(expires 10m;),过期后再走 If-Modified-Since 验证
- 避免给动态接口(如 /api/user)配 expires,除非你能确保内容在指定时间内绝对不变化











