(面试题)什么是 HTTP 缓存?(笔记)

657 阅读6分钟

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. 缓存示例:

假设你第一次访问网站,加载了一张图片:

  1. 服务器返回图片,并附带 Cache-Control: max-age=3600(缓存1小时)。

  2. 1小时内再次访问:浏览器直接读缓存,不请求服务器(强缓存)

  3. 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 示例:

假设你访问一个网站,加载了一张图片:

  1. 服务器返回图片,并设置 Cache-Control: max-age=3600(强缓存 1 小时)。

  2. 1 小时内再次访问:

    • 浏览器发现强缓存有效,直接使用缓存,不会发送请求到服务器。
  3. 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)。
  • 清除浏览器缓存:在浏览器设置中清除缓存数据。
  • 使用隐身模式:打开浏览器的隐身窗口,避免缓存干扰。