协商缓存依赖服务端生成的etag和客户端自动携带的if-none-match请求头配合工作:首次响应返回etag,后续请求浏览器自动带上if-none-match,服务端比对一致则返回304,否则返回200及新etag。

协商缓存依赖服务端生成的 ETag 和客户端携带的 If-None-Match 请求头配合工作。关键不是“设置”某个字段,而是服务端正确生成并校验 ETag,客户端自动参与——浏览器会在有缓存且未过期时自动带上 If-None-Match。
服务端如何生成和返回 ETag
ETag 是资源的唯一标识符,通常基于内容哈希(如 MD5、SHA1)或最后修改时间+大小组合生成。它必须是字符串,建议加引号(弱校验用 W/"xxx",强校验用 "xxx")。
- Node.js(Express)示例:
app.get('/api/data', (req, res) => {
const data = JSON.stringify({ message: 'hello' });
const etag = crypto.createHash('md5').update(data).digest('hex');
res.setHeader('ETag', `"${etag}"`); // 强校验
res.json(JSON.parse(data));
});
服务端如何校验 If-None-Match 并返回 304
当客户端再次请求,且缓存未过期时,浏览器会自动在请求头中带上 If-None-Match: "xxx"。服务端需读取该值,与当前资源的 ETag 比较:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 匹配 → 返回状态码 304 Not Modified,不带响应体,只返回空响应体 + 缓存相关头(如
Cache-Control) - 不匹配或缺失 → 正常返回 200 + 新内容 + 新 ETag
const data = JSON.stringify({ message: 'hello' });
const etag = `"${crypto.createHash('md5').update(data).digest('hex')}"`; // 注意加引号
const ifNoneMatch = req.headers['if-none-match'];
if (ifNoneMatch === etag) {
return res.status(304).end(); // 必须 end(),不能 send()
}
res.setHeader('ETag', etag);
res.json(JSON.parse(data));
});
客户端无需手动设置,但要注意触发条件
浏览器自动管理 If-None-Match,前提是:
- 上一次响应包含有效的
ETag头 - 当前请求是相同 URL 的 GET/HEAD 请求
- 缓存未因
Cache-Control: no-cache或max-age=0被跳过(注意:no-cache 不禁用协商缓存,它只是强制每次验证) - 没有禁用缓存的开发者工具设置(如勾选 “Disable cache”)
ETag 与 Last-Modified 的区别与选择
两者都支持协商缓存,但行为不同:
-
Last-Modified基于时间戳,精度低(秒级),文件内容不变但修改时间变也会误判 -
ETag更精确(可基于内容),适合动态内容或秒级频繁更新的场景 - 若同时存在,
If-None-Match优先于If-Modified-Since校验 - 生产环境建议优先用 ETag,尤其对 API 响应;静态资源可结合构建工具生成稳定哈希 ETag
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










