前言
在前端性能优化中,“网络请求”是耗时大户。如何让浏览器“少请求”甚至“不请求”后端接口,是提升页面加载速度的关键。本文将带你深入理解 浏览器 HTTP 缓存 的两道防线(强缓存与协商缓存),以及 CDN 是如何让资源“飞”到用户面前的
一、 浏览器缓存机制全貌
浏览器缓存的本质是:当浏览器发起请求时,首先在本地查找是否有缓存副本,根据缓存标识决定是否使用副本或向服务器发起新请求。
整个缓存策略可以概括为两步走:
- 强缓存:浏览器直接判断是否有效,有效则直接用,不发请求。
- 协商缓存:强缓存失效后,浏览器发送请求询问服务器“资源变没变”,没变就用缓存(304),变了就返回新资源(200)。
二、 第一道防线:强缓存
强缓存不需要与服务器通信。命中强缓存时,Chrome 控制台的 Network 选项中 Status 会显示 200 (from memory cache) 或 200 (from disk cache)。
1. Expires (HTTP/1.0)
- 原理:服务器返回一个绝对时间戳(例如:
Wed, 22 Oct 2025 08:41:00 GMT)。如果当前本地时间小于这个时间,则缓存有效。 - 缺陷:它严重依赖客户端本地时间。如果用户修改了本地时间,或者客户端与服务端时间不同步,会导致缓存判断出错。
2. Cache-Control (HTTP/1.1)
为了解决 Expires 的缺陷,HTTP/1.1 新增了 Cache-Control 字段。
- 原理:通过
max-age=xxx设置相对时间(单位:秒)。例如max-age=3600代表资源在 3600 秒内有效,与本地时间无关。 - 优先级:当
Cache-Control和Expires同时存在时,Cache-Control优先级更高。
Cache-Control 常用取值辨析:
| 指令 | 含义 |
|---|---|
public | 所有内容都将被缓存(客户端和代理服务器/CDN 均可缓存)。 |
private | 默认值。只有客户端(浏览器)可以缓存,代理服务器不可缓存。 |
no-cache | 客户端缓存内容,但每次使用前必须向服务器发起协商缓存验证。 |
no-store | 禁止任何缓存。每次都向服务器发起全新的请求。 |
max-age=x | 缓存内容将在 x 秒后失效。 |
三、 第二道防线:协商缓存
当强缓存失效(max-age 过期)或设置了 no-cache 时,浏览器会携带缓存标识向服务器发起请求,由服务器决定是否使用缓存。
1. 结果状态码
304 Not Modified:服务器告诉浏览器:“资源没变,你继续用本地缓存吧”。200 OK:服务器告诉浏览器:“资源变了,这是最新的数据”,并更新本地缓存和标识。
2. 标识一:Last-Modified / If-Modified-Since
-
Last-Modified:是服务器响应请求时,返回给浏览器该请求资源在服务器最后被修改的时间。
-
If-Modified-Since:浏览器再次请求时,自动带上上次的
Last-Modified值。通过这个值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求后,会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件。 -
不足:
- 精度问题:时间单位是秒。如果文件在 1 秒内被修改多次,服务端无法感知。
- 内容未变:如果仅仅是打开文件保存了一下(修改时间变了),但内容没变,也会导致缓存失效,浪费带宽。
3. 标识二:ETag / If-None-Match (优先级更高)
为了解决 Last-Modified 的不足,HTTP/1.1 引入了 ETag(实体标签)。
-
ETag:服务端将要返回给客户端的数据通进行哈希计算生成一个Etag字符串(类似文件指纹)。只要内容变了,ETag 必变。
-
If-None-Match:浏览器再次请求时,会将上次请求返回的Etag值包装在If-None-Match中,服务端会检测If-None-Match值是否和Etag一致,如果一致则返回304。
-
不足:
- 性能损耗:生成 ETag 需要额外的计算开销(特别是大文件)。
- 分布式问题:在多台服务器集群中,如果不同服务器对同一个文件的 ETag 计算算法或 inode 不同,可能导致 ETag 不一致而缓存失效。
总结:协商缓存对比
| 特性 | Last-Modified | ETag |
|---|---|---|
| 依据 | 文件最后修改时间 | 文件内容哈希值 |
| 精度 | 秒级(低) | 极高(字节级变化) |
| 优先级 | 低 | 高 |
| 服务端消耗 | 极低 | 较高(需计算) |
四、 跨越千里的加速:CDN 缓存
1. 什么是 CDN?
CDN(Content Delivery Network,内容分发网络)是一种分布式网络架构。简单来说,就是把你的资源“搬”到离用户最近的服务器上。
作用: 解决因地域、带宽、网络拥挤带来的访问延迟,提高响应速度。
2. 通信流程对比
🛑 不使用 CDN 的流程:
- 用户请求 DNS 解析域名。
- DNS 返回源站服务器 IP。
- 用户向该 IP 请求资源。
- 源站服务器返回资源。
✅ 使用 CDN 的流程:
-
DNS 解析:用户向传统 DNS 服务器请求域名解析。
-
CNAME 重定向:DNS 服务器将域名解析权交给 CNAME 指向的 CDN 专用 DNS 服务器。
-
智能调度:CDN 专用 DNS 服务器通过全局负载均衡器,根据用户的 IP 地址(地理位置)、运营商、服务器负载等情况,计算出离用户最近最优的节点。
-
返回节点 IP:将该区域缓存服务器的 IP 返回给用户。
-
请求资源:用户向该缓存服务器发起请求。
- 命中:如果这台服务器有资源,直接返回。
- 回源:如果没有,它会向上一级或源站服务器请求资源(这就叫回源),拉取到本地缓存后,再返回给用户。
四、 面试题:当用户刷新页面(F5)和强制刷新(Ctrl+F5)时,浏览器缓存是如何工作的?
1. F5 刷新(普通刷新)
当你按下 F5 时,浏览器会认为:“虽然我本地有缓存,但我不太确定它是不是最新的,我去问问服务器。”
-
请求头变化:浏览器会自动添加
Cache-Control: max-age=0。 -
结果:跳过强缓存阶段,直接进入协商缓存。
- 如果服务器发现资源没变,返回 304,浏览器继续用本地旧缓存。
- 如果资源变了,返回 200 和新内容。
2.Ctrl + F5(强制刷新)
当你按下 Ctrl + F5 时,浏览器会认为:“我不要本地任何缓存,服务器你必须给我一份最新的!”
- 请求头变化:浏览器会添加
Cache-Control: no-cache和Pragma: no-cache,并且不会携带If-None-Match(ETag) 或If-Modified-Since(Last-Modified)。 - 结果:完全跳过所有缓存逻辑,状态码必定为 200,并重新下载资源。
补充:为什么“地址栏回车”和“F5”不一样?
这是很多人的误区。
- 地址栏输入:是最高级别的缓存利用。只要
max-age没过期,浏览器连服务器的门都不会敲,直接从内存或磁盘拿数据。 - F5 刷新:是主动触发的校验行为。它强制浏览器去敲服务器的门,问一句:“嘿,我这东西还能用吗?”
💡 总结
- 强缓存(Expires/Cache-Control)最快,直接不发请求。
- 协商缓存(Last-Modified/ETag)次之,发请求确认,节省带宽。
- CDN 让资源在物理距离上离用户更近。
- 最佳实践:通常对 HTML 文件使用协商缓存(
no-cache),确保用户及时获取最新引用;对 CSS/JS/图片等静态资源使用强缓存(max-age设置很长)配合文件名 Hash(如style.a1b2c.css)来实现版本更新。