协商缓存需服务端设置Last-Modified(基于文件mtime转UTC字符串)和ETag(内容哈希或size-mtime组合),配合Cache-Control强制验证;浏览器自动携带If-Modified-Since或If-None-Match,服务端严格字符串比对后返回304或200。

协商缓存靠 ETag 和 Last-Modified 配合客户端的 If-None-Match、If-Modified-Since 实现,核心是服务端提供可验证的标识,浏览器自动携带比对。两者不是二选一,而是互补使用。
服务端怎么设 Last-Modified
它基于文件系统修改时间,适合内容与文件 mtime 强绑定的场景:
- 用
fs.statSync或fs.stat读取静态文件元信息,取mtime - 必须调用
.toUTCString()转成标准 HTTP 时间格式(如Wed, 18 Dec 2013 00:32:38 GMT),不能直接 toString 或 toJSON - 通过
res.setHeader('Last-Modified', mtime.toUTCString())写入响应头 - 建议搭配
Cache-Control: no-cache或max-age=0, must-revalidate,确保浏览器进入协商流程,不跳过验证
服务端怎么设 ETag
ETag 是资源内容指纹,适合内容变化但 mtime 不变的情况(比如构建产物含 hash、动态生成脚本):
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 弱 ETag 推荐格式:
W/"size-mtime"(Nginx 默认),或强 ETag:"md5-hash" - Node.js 中可用
crypto.createHash('md5').update(fileContent).digest('hex')计算内容哈希(小文件适用) - 大文件避免全量计算,可改用
size + mtime组合生成弱 ETag,降低开销 - Nginx 只需配置
etag on;,但前提是响应已含Last-Modified头,否则不会生效
浏览器怎么参与协商
你不需要手动加请求头,现代浏览器会自动处理:
立即学习“Java免费学习笔记(深入)”;
- 首次响应带
Last-Modified→ 后续请求自动带If-Modified-Since - 首次响应带
ETag→ 后续请求自动带If-None-Match - 两个头都存在时,浏览器可能同时发送两个验证头;服务端优先校验
If-None-Match,命中即返回 304,不再检查时间戳 - Ajax 请求(fetch / XHR)默认行为一致,无需额外设置;切忌手动加
cache: 'reload'或Cache-Control: max-age=0,这会让浏览器跳过协商直接重发完整请求
常见问题怎么避坑
看似简单,实际容易因细节失败:
- 时间比对必须字符串全等,不能 new Date() 解析后比毫秒数——时区、格式化差异会导致误判
- Last-Modified 精度只有秒级,1 秒内多次改动可能漏判;ETag 正是为弥补这点而存在
- CDN、反向代理、Webpack Dev Server 等中间层可能删掉或覆盖这些头,上线前务必用
curl -I实测响应头 - 不要同时用
add_header ETag和etag on,Nginx 会冲突;也不要给 API 响应硬塞 ETag 却不保证幂等性

















