浏览器缓存机制
强缓存与协商缓存的工作原理和使用场景
问题
浏览器缓存分为强缓存和协商缓存,它们分别是如何工作的?Cache-Control、Expires、ETag、Last-Modified 各有什么作用?
解答
缓存流程
请求资源
↓
检查强缓存(Cache-Control/Expires)
↓
命中 → 直接使用缓存(200 from cache)
未命中 ↓
发送请求,携带协商缓存标识(If-None-Match/If-Modified-Since)
↓
服务器验证
↓
未修改 → 304 Not Modified,使用缓存
已修改 → 200 OK,返回新资源
强缓存
不向服务器发送请求,直接从缓存读取资源。
# Expires(HTTP/1.0)- 绝对时间,受客户端时间影响
Expires: Wed, 21 Oct 2025 07:28:00 GMT
# Cache-Control(HTTP/1.1)- 相对时间,优先级更高
Cache-Control: max-age=31536000
Cache-Control 常用指令:
# 缓存 1 年
Cache-Control: max-age=31536000
# 禁止缓存
Cache-Control: no-store
# 每次都要验证(走协商缓存)
Cache-Control: no-cache
# 只能被浏览器缓存,不能被 CDN 缓存
Cache-Control: private
# 可以被 CDN 等中间代理缓存
Cache-Control: public
协商缓存
向服务器发送请求,由服务器判断是否使用缓存。
Last-Modified / If-Modified-Since:
# 首次响应,服务器返回资源最后修改时间
Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT
# 再次请求,浏览器携带该时间
If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT
ETag / If-None-Match:
# 首次响应,服务器返回资源唯一标识
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
# 再次请求,浏览器携带该标识
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Node.js 实现示例
const http = require('http');
const fs = require('fs');
const crypto = require('crypto');
http.createServer((req, res) => {
const filePath = './static' + req.url;
const stat = fs.statSync(filePath);
const content = fs.readFileSync(filePath);
// 生成 ETag
const etag = crypto.createHash('md5').update(content).digest('hex');
// 获取最后修改时间
const lastModified = stat.mtime.toUTCString();
// 检查协商缓存
if (req.headers['if-none-match'] === etag ||
req.headers['if-modified-since'] === lastModified) {
res.writeHead(304);
res.end();
return;
}
// 设置缓存头
res.setHeader('Cache-Control', 'max-age=3600'); // 强缓存 1 小时
res.setHeader('ETag', etag);
res.setHeader('Last-Modified', lastModified);
res.end(content);
}).listen(3000);
实际应用策略
# Nginx 配置示例
# HTML - 不缓存或短时间缓存
location ~* \.html$ {
add_header Cache-Control "no-cache";
}
# 带 hash 的静态资源 - 长期缓存
location ~* \.(js|css|png|jpg|gif|ico)$ {
add_header Cache-Control "max-age=31536000, immutable";
}
# API 接口 - 不缓存
location /api/ {
add_header Cache-Control "no-store";
}
关键点
- 强缓存优先级:Cache-Control > Expires,前者用相对时间更可靠
- 协商缓存优先级:ETag > Last-Modified,前者基于内容 hash 更精确
- Last-Modified 缺陷:精度只到秒级,文件内容不变但修改时间变了也会失效
- 缓存策略:HTML 用 no-cache 保证更新,带 hash 的静态资源用长期缓存
- 304 vs 200:304 只返回响应头,节省带宽;200 from cache 完全不发请求
目录