强缓存优先于协商缓存:浏览器先检查cache-control或expires,命中则直接返回200(from cache);未命中才发起带if-none-match/if-modified-since的请求,由服务器决定返回200或304。

强缓存和协商缓存不是“二选一”的配置问题,而是浏览器默认按顺序执行的两级判断机制:先查强缓存,命中就直接用(不发请求);不命中才走协商缓存(发请求,由服务器决定返回 200 还是 304)。实际项目里,两者常一起配,但配置逻辑完全不同,错配会导致资源长期不更新或白白多一次请求。
Cache-Control 和 Expires 哪个该用?
只用 Cache-Control,别依赖 Expires。前者是相对时间(如 max-age=3600),从响应那一刻开始算,不受客户端时钟影响;后者是绝对时间(如 Expires: Tue, 28 Nov 2025 10:00:00 GMT),一旦用户手动调快系统时间,缓存就可能提前失效或死扛过期。即使两个都设,Cache-Control 也永远优先生效。
常见错误:
- 在 webpack 打包后静态资源上只写
Expires,没设Cache-Control→ CDN 或现代浏览器可能忽略它 - 误把
no-cache当作“禁止缓存” → 它其实允许缓存,但强制每次走协商流程,等效于只开协商缓存 - 对 HTML 文件也设了
max-age=86400→ 用户改了页面,浏览器在 24 小时内根本看不到更新
Last-Modified 和 ETag 怎么选?
Last-Modified 看文件修改时间戳,简单但精度低(秒级,且无法识别内容未变但文件重写的情况);ETag 是服务端生成的资源指纹(比如基于文件内容的 hash),更准确,但计算成本略高。两者可以共存,浏览器会优先用 ETag 验证(如果都存在)。
实操建议:
- 静态资源(JS/CSS/图片)推荐同时返回
ETag和Last-Modified,兼容性好,且服务端 Nginx/Apache 默认就支持 - API 接口响应慎用
Last-Modified→ 数据库记录没“最后修改时间”字段的话,硬塞一个时间容易出错 - 如果用了构建哈希(如
main.a1b2c3.js),可考虑关掉ETag→ 文件名已唯一,协商缓存意义不大,省一次比对开销
为什么加了 max-age 还是看到 304?
说明强缓存根本没生效。常见原因有:
- 响应头里写了
Cache-Control: no-cache或must-revalidate→ 浏览器跳过强缓存阶段,直接进协商流程 - 请求是开发者工具里点「Disable cache」后发的 → 强制禁用所有缓存,跟配置无关
- 资源被标记为
private且走代理(如公司内网网关)→ 某些中间件会主动剥离Cache-Control - HTTP/2 Server Push 推送的资源,部分浏览器不按常规缓存逻辑处理
验证方式:打开 Chrome DevTools → Network 标签 → 找对应请求 → 看 Headers → Response Headers 里有没有 Cache-Control,值是否含 max-age= 且数值合理;再看该请求的 Size 列是否显示 from memory cache 或 from disk cache。
HTML 文件到底该不该加强缓存?
不该。HTML 几乎总是要禁用强缓存,否则用户刷新页面也看不到新 JS/CSS 的加载效果。正确做法是:
- HTML 响应头设
Cache-Control: no-cache(触发协商缓存)或max-age=0, must-revalidate - 确保服务器对 HTML 返回
ETag或Last-Modified→ 这样每次请求仍是 1 个 HTTP 往返,但内容不变时返回 304,不传正文 - 静态资源(JS/CSS/图片)用哈希命名 +
Cache-Control: public, max-age=31536000→ 彻底交由强缓存管理,永不 304
最容易被忽略的一点:Nginx 或 CDN 的缓存策略可能覆盖你应用层设置的响应头。上线前务必 curl 一下真实响应头,别只信本地开发环境。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











