1. 什么是 HTTP 缓存
HTTP 缓存又叫 浏览器缓存,它将页面上长时间不更新的资源缓存到浏览器上,下次访问页面时该部分资源就直接从缓存中获取,从而减少了网络请求的次数,提高了页面的加载速度。
举个例子来说:假设你每天都要去图书馆(服务器)借同一本书(网页资源),每次借书都要跑一趟,太麻烦了。于是你想到:把书复印一份放在书包里(缓存) ,下次需要时直接从书包拿,不用再跑图书馆了!
缓存的好处就是:速度快、省流量、减轻服务器压力。
2. 缓存存在哪里
-
浏览器缓存:存在你电脑/手机里(比如 Chrome 的临时文件)。
-
中间代理缓存:比如公司网络的缓存服务器。
-
CDN 缓存:类似分布在各地的“图书馆分馆”,离你更近。
3. 缓存怎么工作?
浏览器和服务器通过 HTTP 协议头 沟通缓存规则,关键字段如下:
(1) Cache-Control(最强缓存控制)
max-age=3600:这份资源 1 小时内有效,直接从缓存拿,不找服务器。no-cache:缓存可以存,但每次用之前要问服务器“有没有更新?”。no-store:禁止缓存,敏感数据(如银行卡号)用这个。
通过浏览器url地址栏请求的资源,请求头中就会自动携带 Cache-Control:max-age=0 字段,也就意味着这种资源无法被强缓存
被缓存的资源在浏览器的 cache storage 中,本质上还是在硬盘上
强制刷新浏览器,会清空浏览器的 cache storage
(2) Expires(过期时间)
- 例如
Expires: Wed, 21 Oct 2025 07:28:00 GMT,告诉浏览器在这个时间之前直接用缓存。
(3) ETag 和 Last-Modified(协商缓存)
ETag:资源的“指纹”(如版本号)。浏览器下次请求时会带If-None-Match头,如果指纹没变,服务器返回304 Not Modified,继续用缓存。Last-Modified:资源最后修改时间。浏览器下次请求带If-Modified-Since头,如果时间没变,服务器返回304。
res.writeHead(200, {
'Content-Type': mime.getType(ext),
'Cache-Control': 'max-age=1000000',
'etag': sum,
})
或
es.writeHead(status, {
'content-type': mime.getType(ext),
'cache-control': 'max-age=86400', // 强缓存 1 天
'last-modified': stats.mtimeMs // 最后修改时间
})
4. 缓存示例:
假设你第一次访问网站,加载了一张图片:
-
服务器返回图片,并附带
Cache-Control: max-age=3600(缓存1小时)。 -
1小时内再次访问:浏览器直接读缓存,不请求服务器(强缓存) 。
-
1小时后再次访问:浏览器问服务器:“图片有变化吗?”(带
If-None-Match或If-Modified-Since)。- 如果没变化:服务器返回
304,继续用缓存(协商缓存)。 - 如果变化了:返回新图片,更新缓存。
- 如果没变化:服务器返回
5. 强缓存 和 协商缓存 的优先级
5.1 优先级 强缓存 > 协商缓存
-
强缓存优先级更高:浏览器会先检查强缓存是否有效,如果有效,直接使用缓存,不会发送请求到服务器。
-
强缓存失效后:才会走协商缓存流程,向服务器发送请求,询问资源是否有更新。
5.2 具体流程
以下是浏览器处理缓存的完整流程:
步骤 1:检查强缓存
-
浏览器检查资源的
Cache-Control或Expires字段,判断是否在有效期内。- 如果强缓存有效(例如
max-age未过期),直接使用缓存,不会发送请求到服务器。 - 如果强缓存失效(例如
max-age过期),进入下一步。
- 如果强缓存有效(例如
步骤 2:检查协商缓存
-
浏览器向服务器发送请求,并带上
If-None-Match(对应ETag)或If-Modified-Since(对应Last-Modified)字段。- 如果资源未更新(服务器返回
304 Not Modified),继续使用缓存。 - 如果资源已更新(服务器返回
200 OK和新资源),更新缓存并使用新资源。
- 如果资源未更新(服务器返回
5.3 示例:
假设你访问一个网站,加载了一张图片:
-
服务器返回图片,并设置
Cache-Control: max-age=3600(强缓存 1 小时)。 -
1 小时内再次访问:
- 浏览器发现强缓存有效,直接使用缓存,不会发送请求到服务器。
-
1 小时后再次访问:
- 浏览器发现强缓存失效,向服务器发送请求,带上
If-None-Match或If-Modified-Since。 - 如果图片未更新,服务器返回
304,继续使用缓存。 - 如果图片已更新,服务器返回新图片,更新缓存并使用新图片。
- 浏览器发现强缓存失效,向服务器发送请求,带上
5.4 为什么强缓存优先级更高
-
性能优化:强缓存完全不发送请求,速度最快,能最大程度减少网络开销。
-
减少服务器压力:强缓存有效时,服务器完全不需要处理请求。
5.5 注意事项
-
强缓存和协商缓存可以同时存在:比如设置
Cache-Control: max-age=3600(强缓存)和ETag(协商缓存)。 -
强缓存失效后才会触发协商缓存:如果强缓存有效,协商缓存的字段(如
ETag)根本不会被使用。
6. 如果在强缓存生效时间内资源发生了变更怎么解决
6.1 修改资源 URL(最常用)
通过修改资源的 URL 来强制浏览器获取新资源。常见的方式有:
- 文件名加版本号:例如
style_v1.css→style_v2.css。 - 文件名加哈希值:例如
style.a1b2c3.css(哈希值根据文件内容生成,内容变化时哈希值也会变化)。 - URL 加查询参数:例如
style.css?v=1→style.css?v=2。
原理
- URL 变化后,浏览器会认为这是一个全新的资源,从而绕过缓存,直接请求新资源。
- 旧资源的缓存仍然存在,但不会被使用。
示例
<!-- 旧资源 -->
<link rel="stylesheet" href="style.css?v=1">
<!-- 资源更新后 -->
<link rel="stylesheet" href="style.css?v=2">
6.2 缩短强缓存时间
如果资源更新频率较高,可以缩短强缓存时间(例如 max-age=60,缓存 1 分钟),让浏览器更频繁地检查更新。
适用场景
- 动态资源(如 API 响应)。
- 不适合静态资源(如 CSS、JS、图片),因为频繁请求会增加服务器压力。
6.3 使用 Cache-Control: no-cache
设置 Cache-Control: no-cache,让浏览器每次使用缓存前都向服务器验证资源是否有更新。
原理
- 浏览器会缓存资源,但每次使用时都会发送请求到服务器,检查资源是否更新(通过
ETag或Last-Modified)。 - 如果资源未更新,服务器返回
304 Not Modified,浏览器继续使用缓存。 - 如果资源已更新,服务器返回新资源。
6.4 使用 Cache-Control: must-revalidate
设置 Cache-Control: must-revalidate,让浏览器在强缓存过期后必须向服务器验证资源是否有更新。
原理
- 强缓存有效期内,浏览器直接使用缓存。
- 强缓存过期后,浏览器必须向服务器验证资源是否有更新。
6.5 手动清除缓存
在开发或测试阶段,可以通过以下方式手动清除缓存:
- 浏览器强制刷新:按
Ctrl + F5(Windows)或Cmd + Shift + R(Mac)。 - 清除浏览器缓存:在浏览器设置中清除缓存数据。
- 使用隐身模式:打开浏览器的隐身窗口,避免缓存干扰。