协商缓存生效需满足两个前提:一是强缓存已失效或被禁用(如Cache-Control: no-cache),二是服务端至少返回Last-Modified或ETag标识,且浏览器自动携带对应验证头(If-Modified-Since或If-None-Match)发起请求。

用户频繁刷新页面时,浏览器默认会尝试复用缓存资源。若配置得当,协商缓存能显著减少服务器带宽消耗——它不传输资源主体,只做轻量验证,命中时返回 304 Not Modified,浏览器直接读本地副本。
协商缓存生效的前提条件
它不会自动启用,必须满足两个关键前提:
-
强缓存已失效或被禁用:比如响应头中设置了
Cache-Control: no-cache或max-age=0,浏览器才会在每次刷新时发起请求并带上验证头;如果强缓存还有效(如max-age=3600),连请求都不会发,根本走不到协商阶段。 -
服务端正确返回资源标识:至少提供
Last-Modified(文件最后修改时间)或ETag(资源唯一指纹)中的一个;两者都提供时,ETag优先级更高、更精确(支持内容变化但修改时间未变的场景)。
客户端行为:浏览器自动携带验证头
当强缓存失效后,浏览器在发起新请求时,会自动附加以下请求头之一(取决于服务端上次返回了哪个标识):
- 如果服务端返回了
Last-Modified: Wed, 10 Jul 2026 08:22:15 GMT,浏览器下次请求会带上:If-Modified-Since: Wed, 10 Jul 2026 08:22:15 GMT - 如果服务端返回了
ETag: "abc123",浏览器下次请求会带上:If-None-Match: "abc123"
这个过程完全由浏览器自动完成,无需前端 JavaScript 干预或手动设置请求头。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
服务端如何高效响应验证请求
服务端收到带验证头的请求后,只需比对、不传正文,就能节省全部带宽:
- 比对
If-Modified-Since和当前资源真实修改时间:一致则返回 304 状态码 + 空响应体,不发送 JS/CSS 文件内容。 - 比对
If-None-Match和当前资源计算出的 ETag(例如基于文件内容 hash):匹配则同样返回 304。 - 任一比对不通过,说明资源已更新,才正常返回 200 + 新资源 + 新的 Last-Modified/ETag。
例如 Express 中静态 JS 文件可这样处理(简化逻辑):
app.get('/static/app.js', (req, res) => {
const filePath = './public/app.js';
const stat = fs.statSync(filePath);
const etag = crypto.createHash('md5').update(fs.readFileSync(filePath)).digest('hex');
res.setHeader('ETag', `"${etag}"`);
res.setHeader('Cache-Control', 'no-cache'); // 确保进入协商流程
if (req.headers['if-none-match'] === `"${etag}"`) {
res.status(304).end(); // 不发任何文件内容
} else {
res.sendFile(filePath); // 只有这时才传输文件
}
});
为什么 ETag 比 Last-Modified 更适合 JS/CSS 文件
JavaScript 和 CSS 文件常因构建工具(如 Webpack/Vite)生成内容相同但时间戳变动的产物。此时 Last-Modified 会频繁变化,导致协商缓存失灵;而 ETag 基于文件内容生成,内容不变则 ETag 不变,缓存命中率更高。生产环境建议优先使用 ETag 配合 no-cache,而非依赖修改时间。

















