精确控制协商缓存需保留强缓存基础,用no-cache/must-revalidate触发协商,immutable阻断冗余协商,配合强ETag与Vary确保验证精准性。

要精确控制浏览器的 HTTP 缓存协商行为,关键不是“禁用强缓存”,而是**保留强缓存基础,同时通过特定参数主动触发、约束或优化协商过程**。协商缓存本身不独立存在——它只在强缓存失效(或被显式绕过)后才启动。因此,“控制协商行为”的本质,是精准设计 Cache-Control 指令组合,并配合 ETag 或 Last-Modified 的生成逻辑与语义。
明确触发协商的 Cache-Control 指令
以下指令会强制浏览器进入协商流程,但行为有明显差异:
- no-cache:最常用也最易误解。它不禁止缓存,而是禁止强缓存直接使用。每次请求都必须携带 If-None-Match 或 If-Modified-Since 向服务器验证。适合内容可能随时更新但变化不频繁的资源(如首页 HTML、用户仪表盘)。
- max-age=0:效果类似 no-cache,但语义更偏向“立即过期”,仍允许协商。部分旧客户端兼容性略好,但现代实践中推荐优先用 no-cache。
- must-revalidate:不阻止强缓存生效,但一旦 max-age 过期,浏览器必须验证**(不能跳过或凭用户刷新就忽略)**。适合对时效性敏感又需兼顾首屏性能的资源(如股票行情 JS 配置)。
用 immutable 阻断不必要的协商
对于内容确定永不变更的静态资源(如哈希命名的 JS/CSS 文件:app.8a2f1e.js),应搭配 immutable:
- Cache-Control: public, max-age=31536000, immutable
- immutable 的作用是:即使用户按 F5 刷新或点击“重新加载”,浏览器也不会发送任何协商请求头(If-None-Match / If-Modified-Since 被完全忽略)。
- 这能彻底消除协商开销,同时保持一年强缓存的有效性,是现代前端构建的标准实践。
协同 ETag 精准控制验证粒度
协商是否命中,最终由服务器根据请求头判断。Cache-Control 只管“要不要发”,ETag 才决定“能不能命”。要精确控制行为:
- 对内容敏感型资源(如 API 响应、模板渲染结果),用强 ETag(基于完整响应体哈希),确保内容微调即失效;
- 对时间敏感型资源(如带时间戳的公告),可结合
Vary: Date或动态生成 ETag(如ETag: "v1-20260519"),让协商结果随业务逻辑变化; - 避免仅依赖 Last-Modified:其秒级精度无法捕获高频更新,且文件重写可能导致时间戳变而内容不变,引发误判。
避免协商被意外跳过的配置陷阱
以下常见错误会让协商形同虚设:
- 同时设置
Cache-Control: no-cache和Expires: past-time:虽无害,但冗余; - 服务器未返回 ETag 或 Last-Modified:no-cache 下浏览器仍会发请求,但服务器只能返回 200,无法返回 304,协商失效;
- CDN 或反向代理(如 Nginx)覆盖了源站 ETag:需确认 CDN 配置未删除或篡改 ETag 头;
- 响应中漏掉
Vary:当资源根据 User-Agent、Accept-Encoding 等动态生成时,必须设置Vary: User-Agent, Accept-Encoding,否则不同设备可能共用同一份缓存并错误协商。


















