浏览器缓存是前端面试里最经典的 HTTP 题之一。很多人会背“强缓存和协商缓存”,但如果面试官继续追问
Cache-Control、ETag、Vary、CDN 缓存、资源更新策略,回答往往就会断掉。
这篇文章的目标不是背答案,而是把浏览器缓存真正讲透。
一、先说结论:浏览器缓存到底解决什么问题?
浏览器缓存的本质目标有三个:
- 减少重复请求
- 降低网络延迟
- 减轻服务器压力
如果没有缓存,一个用户每次刷新页面都要重新拉取:
- HTML
- CSS
- JavaScript
- 图片
- 字体
- 接口返回结果
这不仅浪费流量,也会让页面变慢。
所以缓存本质上就是一句话:
如果资源没变,就尽量不要重新下载。
二、浏览器缓存并不是只有一种
HTTP 缓存里,常见要分清两个概念:
2.1 私有缓存(Private Cache)
也就是浏览器本地缓存。
特点:
- 只属于当前用户
- 可以缓存个性化内容
- 常见于静态资源、页面、接口结果
2.2 共享缓存(Shared Cache)
也就是位于客户端和源站之间的缓存,例如:
- CDN
- 反向代理
- 网关缓存
特点:
- 多个用户共享
- 更适合缓存公共资源
- 需要特别注意个性化内容泄漏风险
MDN 对缓存类型的示意图如下:
这也是为什么缓存问题不能只谈“浏览器有没有命中”,还要考虑:
- CDN 是否命中
- 浏览器是否本地命中
- 是否需要回源校验
三、浏览器缓存整体流程
最经典的缓存流程可以简化成这样:
第一次请求:
- 浏览器请求资源
- 服务器返回资源和缓存相关响应头
- 浏览器保存资源和元信息
后续再次请求时:
- 浏览器先看本地缓存是否还能直接用
- 如果还能直接用,就不发请求
- 如果不能直接用,就带上条件请求头去问服务器
- 服务器告诉浏览器“没变”还是“变了”
于是就形成了两个核心机制:
- 强缓存
- 协商缓存
四、强缓存:本地还新鲜,就直接用
强缓存的特点是:
命中后,浏览器直接使用本地副本,不发送请求到服务器。
这也是最省的一种缓存方式。
4.1 强缓存依赖哪些响应头?
主要是两个:
Cache-ControlExpires
其中现代开发里更推荐 Cache-Control。
4.2 Expires
这是 HTTP/1.0 的老方案,用一个明确时间表示过期时间:
Expires: Tue, 28 May 2026 12:00:00 GMT问题在于它依赖客户端和服务端时间同步,系统时钟偏差会带来问题。
4.3 Cache-Control
这是现代缓存控制的核心。
最常见写法:
Cache-Control: max-age=3600表示这个响应在 3600 秒内是新鲜的,可以直接使用。
也就是说,只要资源年龄还没超过 max-age,浏览器就不会重新请求服务器。
五、Cache-Control 常见指令详解
5.1 max-age
Cache-Control: max-age=604800表示资源在 604800 秒(7 天)内都是 fresh 的。
5.2 public
Cache-Control: public, max-age=604800表示响应可以被共享缓存(如 CDN)缓存。
5.3 private
Cache-Control: private表示只能被浏览器私有缓存缓存,不能被共享缓存缓存。
常用于:
- 用户资料页
- 登录后的个性化响应
- 带 cookie 且内容因用户而异的页面
5.4 no-cache
这是一个非常容易被误解的指令。
Cache-Control: no-cache它的意思不是“不缓存”,而是:
可以缓存,但每次使用前都必须向服务器重新验证。
也就是说,它更接近“必须协商后才能用”,而不是“完全不能存”。
5.5 no-store
Cache-Control: no-store这个才是真正的:
不要存。
适合:
- 银行交易页
- 极高敏感信息页面
- 不希望落地任何缓存副本的响应
5.6 immutable
Cache-Control: public, max-age=31536000, immutable它的意思是:
在 fresh 期间,这个资源肯定不会变。
特别适合带 hash 的静态资源:
app.8a72f3.jsmain.2ef31c.css
这种资源一旦内容变化,文件名也会变化,因此可以安全地配一个很长的缓存时间。
5.7 stale-while-revalidate
Cache-Control: max-age=600, stale-while-revalidate=60表示:
- 前 600 秒内正常 fresh
- 过期后 60 秒内,可以先返回旧内容,同时后台重新验证
这是一种兼顾速度与新鲜度的策略。
六、强缓存什么时候会失效?
强缓存的判断核心是:
资源是否仍然 fresh。
一旦过期,浏览器就不能直接用本地副本了,下一步会进入:
- 协商缓存
- 或直接重新下载
七、协商缓存:资源过期了,但不一定要重下
协商缓存的特点是:
浏览器会向服务器发请求,但会带上“条件”,让服务器判断资源是否真的变了。
如果没变,服务器只返回:
304 Not Modified浏览器继续使用本地缓存副本。
所以协商缓存的价值是:
- 虽然还要发请求
- 但不用重新传完整资源内容
八、协商缓存依赖哪些响应头和请求头?
有两套常见方案:
8.1 Last-Modified / If-Modified-Since
服务器第一次响应:
Last-Modified: Wed, 29 May 2026 10:00:00 GMT下次请求时,浏览器会带上:
If-Modified-Since: Wed, 29 May 2026 10:00:00 GMT服务器检查资源最后更新时间:
- 如果没变,返回
304 - 如果变了,返回新的
200和新内容
8.2 ETag / If-None-Match
服务器第一次响应:
ETag: "f4a1b9"下次请求时浏览器带上:
If-None-Match: "f4a1b9"服务器比较当前资源标识:
- 一样:返回
304 - 不一样:返回
200和新资源
九、ETag 和 Last-Modified 有什么区别?
Last-Modified 的优点
- 简单
- 成本低
- 很多静态资源天然适合
Last-Modified 的缺点
- 精度通常到秒
- 秒级内多次修改可能检测不准
- 内容没变但文件时间变了,也会误判为“变了”
ETag 的优点
- 更精确
- 更适合判断“内容是否真的变化”
ETag 的缺点
- 服务端需要生成标识
- 某些分布式场景下处理不当会带来额外复杂度
实战建议
常见优先级一般是:
ETag > Last-Modified
如果两者都有,通常优先使用 ETag 做判断。
十、强缓存和协商缓存的关系
很多人会把它们当成二选一,其实不是。
常见流程是:
- 先看强缓存
- 如果强缓存命中,直接用本地资源
- 如果强缓存没命中,再走协商缓存
- 如果协商成功,返回
304 - 如果协商失败,返回新的
200
所以更准确地说:
- 强缓存是第一层
- 协商缓存是第二层
十一、浏览器缓存、CDN 缓存、Service Worker 缓存有什么区别?
11.1 浏览器缓存
- 用户本地
- 属于私有缓存
- 主要由 HTTP 头控制
11.2 CDN 缓存
- 位于边缘节点
- 属于共享缓存
- 常结合
Cache-Control、CDN 控制台规则使用
11.3 Service Worker 缓存
- 由前端代码主动控制
- 可以实现离线缓存、预缓存、运行时缓存
- 适合做更灵活的缓存策略
换句话说,HTTP 缓存是浏览器和中间层默认行为,而 Service Worker 缓存是前端主动编排的缓存逻辑。
十二、Vary 为什么重要?
如果同一个 URL 在不同条件下返回不同内容,就不能只按 URL 缓存。
例如同一个地址会根据:
- 语言
- 编码
- 设备能力
返回不同内容。
这时就需要:
Vary: Accept-Language这表示缓存键不能只看 URL,还要把 Accept-Language 一起算进去。
否则就可能出现:
- 中文用户拿到英文缓存
- 移动端拿到桌面端内容
十三、真实项目中的缓存更新策略
面试里如果只会说原理,往往还不够。更高分的回答应该讲:
线上资源怎么安全更新?
13.1 对 HTML:短缓存或不强缓存
HTML 通常变化频繁,且它引用整个资源入口,因此一般不建议超长强缓存。
常见策略:
Cache-Control: no-cache这样浏览器每次都会向服务器确认 HTML 是否更新。
13.2 对 JS / CSS / 图片:长缓存 + 文件名 hash
例如:
app.89d12.jsvendor.a83f1.css
策略通常是:
Cache-Control: public, max-age=31536000, immutable这样做的逻辑是:
- 只要文件名不变,就说明内容不变
- 内容变了,构建产物 hash 也变,URL 自动变化
这就是最经典的 cache busting。
13.3 对接口:按业务类型区分
例如:
- 用户资料:
private, no-cache - 新闻列表:短
max-age或 SWR - 配置接口:可协商缓存
- 金融交易:
no-store
十四、前端面试里最常见的几个误区
14.1 no-cache 不是不缓存
它是“先存下来,但每次用前都校验”。
14.2 no-store 才是真不缓存
这个一定要分清。
14.3 Expires 不是首选
现代开发优先用 Cache-Control: max-age。
14.4 强缓存和协商缓存不是对立关系
它们通常是串联关系,不是互斥关系。
14.5 只谈浏览器缓存不够
真实线上环境里,经常还要结合:
- CDN
- Nginx
- 反向代理
- Service Worker
一起看缓存策略。
十五、面试里怎么回答“浏览器缓存机制”?
如果是面试题,我建议按下面结构回答:
第一步:先讲目标
浏览器缓存的目的是减少重复请求、提升访问速度、降低服务器压力。
第二步:讲两层机制
浏览器缓存分为:
- 强缓存
- 协商缓存
强缓存命中时浏览器直接使用本地副本,不发请求;协商缓存则会发条件请求,由服务器判断资源是否变化。
第三步:讲关键头部
强缓存:
Cache-ControlExpires
协商缓存:
ETag/If-None-MatchLast-Modified/If-Modified-Since
第四步:讲实际更新策略
真实项目里通常会把:
- HTML 配成
no-cache - 带 hash 的静态资源配成长缓存
max-age + immutable
第五步:补一个进阶点
如果内容会因为语言、编码等条件变化,还要配合 Vary;线上还要结合 CDN 缓存一起设计。
这样回答,基本就已经很完整了。
十六、总结
浏览器缓存机制可以浓缩成一句话:
优先直接复用本地副本,实在不能直接复用时,再低成本地向服务器确认资源是否变化。
展开以后就是完整体系:
- 强缓存控制“能不能直接用”
- 协商缓存控制“过期后能不能继续用旧副本”
Cache-Control是现代缓存核心ETag和Last-Modified负责条件验证Vary负责多版本缓存隔离- 实战中要结合 HTML、静态资源、接口、CDN 一起设计
真正理解了这套机制,浏览器缓存这道题就不再是“背概念”,而是一个完整的工程问题。