协商缓存由浏览器自动发起、Nginx 配合响应头实现,关键在于正确提供 ETag/Last-Modified 并响应 304;带哈希的 JS 文件应禁用协商缓存并启用 immutable 强缓存。

JavaScript 中的协商缓存本身不依赖前端代码主动设置,而是由浏览器自动发起、服务端(如 Nginx)配合响应头完成的。关键在于:Nginx 正确提供 ETag 或 Last-Modified,并在客户端携带 If-None-Match / If-Modified-Since 时返回 304。JavaScript 文件作为静态资源,其协商缓存行为完全由 Nginx 的响应头和客户端请求逻辑决定。
Nginx 默认已支持协商缓存,但需确认启用
Nginx 默认开启 etag on 和 if_modified_since exact,这意味着:
- 对于文件系统中的真实静态文件(如
/static/app.js),Nginx 会自动生成ETag(基于文件内容和修改时间的哈希)和Last-Modified(基于文件 mtime) - 浏览器第二次请求时,若缓存未过期且未禁用,会自动带上
If-None-Match或If-Modified-Since - Nginx 内部比对后,匹配即返回
304 Not Modified,不传输 JS 内容,节省带宽与时间
无需额外配置即可生效,但建议显式确认:
location ~* \.js$ {
etag on;
if_modified_since exact;
# 其他缓存头(可选)
add_header Cache-Control "public, max-age=3600, must-revalidate";
}⚠️ 注意:
must-revalidate表示缓存过期后必须验证,它不会阻止协商缓存,反而鼓励浏览器发起条件请求 —— 这正是 JS 文件(尤其未哈希命名时)需要的行为。立即学习“Java免费学习笔记(深入)”;
配合构建哈希的 JS 文件,应禁用协商缓存
如果 JS 文件已通过 Webpack/Vite 输出为带 contenthash 的路径(如 app.a1b2c3.js),则该文件内容不变时 URL 永远不变,适合强缓存:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
location ~* \.[a-f0-9]{8,}\.js$ {
add_header Cache-Control "public, max-age=31536000, immutable";
etag off; # 可选:immutable 已承诺永不变更,ETag 多余
}此时 immutable 指令会让现代浏览器跳过所有条件请求(包括 If-None-Match),彻底避免协商开销 —— 这是性能最优解。
不要手动覆盖 ETag 或禁用协商缓存,除非有明确理由
以下做法通常有害:
-
etag off;:关闭 ETag 后,只剩Last-Modified,而它的秒级精度在高频构建或 NFS 环境下易误判(内容未变但时间戳更新 → 返回 200) -
add_header ETag "";:清空 ETag 会导致协商失败,浏览器退回到完整下载 - 在 JS 响应中硬编码
Cache-Control: no-cache:强制每次走协商,失去 304 节省,还增加服务器压力
正确做法是让 Nginx 自动管理 ETag,仅通过 max-age + must-revalidate 控制协商时机。
验证 JS 文件是否走协商缓存
部署后务必检查实际响应:
- 打开 Chrome DevTools → Network → 刷新页面
- 找到一个
.js请求 → 查看 Response Headers- 应有
ETag: "xyz..."或Last-Modified: ...
- 应有
- 第二次加载时,该请求状态码应为
304,Size 显示(from memory cache)或(from disk cache) - 点开该请求 → Headers → Request Headers → 确认含
If-None-Match: "xyz..."
也可用命令行快速验证:
curl -I -H "If-None-Match: \"abc123\"" https://yoursite.com/app.js # 若返回 304,说明协商正常;若返回 200,检查 ETag 是否一致、文件是否被覆盖等
不复杂但容易忽略

















