HTTP 完全指南(二):缓存机制深度详解
HTTP缓存机制是提升网页性能的关键技术,分为强缓存和协商缓存两类。强缓存通过Expires(绝对时间)或Cache-Control(推荐,相对时间)控制,有效期内直接从本地读取(200 from cache)。协商缓存在缓存过期后触发,通过Last-Modified/ETag向服务器验证资源是否变更,返回304(继续用缓存)或200(下载新资源)。实际应用中,建议:HTML使用no-cache确
引言
上一篇我们学习了 HTTP 的报文结构。今天要讲的是 HTTP 中最实用的优化手段——缓存。
为什么网页第二次打开比第一次快?为什么浏览器有时显示"从缓存加载"?为什么改完代码刷新页面还是旧内容、需要"强制刷新"?这些都是 HTTP 缓存在起作用。
理解缓存机制,不仅能帮你写出加载更快的网页,还能让你在遇到"缓存问题"时快速定位原因,而不是盲目地"清缓存、重启浏览器"。

第一部分:缓存的分类
HTTP 缓存分为两大类:强缓存和协商缓存。

| 缓存类型 | 是否发请求 | 状态码 | 控制字段 |
|---|---|---|---|
| 强缓存 | ❌ 不发 | 200 (from disk cache) | Expires / Cache-Control |
| 协商缓存 | ✅ 发 | 304 Not Modified | Last-Modified / ETag |
第二部分:强缓存
强缓存 = 在缓存有效期内,直接使用本地缓存,不向服务器发请求。
一、Expires(HTTP/1.0)
响应头:
Expires: Mon, 02 Jun 2025 10:00:00 GMT
含义:在 Mon, 02 Jun 2025 10:00:00 GMT 之前,直接使用缓存,不请求服务器。
致命缺陷:Expires 使用绝对时间。如果客户端和服务器时间不同步(比如客户端时间比服务器快 5 分钟),缓存可能提前失效或过期后仍被使用。
二、Cache-Control(HTTP/1.1,推荐)
响应头:
Cache-Control: max-age=3600
含义:从请求时刻算起,缓存有效 3600 秒(1 小时)。这是相对时间,完美解决了 Expires 的时间同步问题。
Cache-Control 常用指令:
| 指令 | 含义 | 方向 |
|---|---|---|
max-age=秒 |
缓存有效期(相对时间) | 响应 |
public |
任何缓存都可以存储(浏览器+CDN) | 响应 |
private |
只能浏览器缓存,不能 CDN 缓存 | 响应 |
no-cache |
可以缓存,但每次必须验证 | 请求/响应 |
no-store |
完全不缓存 | 请求/响应 |
must-revalidate |
过期后必须重新验证 | 响应 |


三、Expires 和 Cache-Control 同时存在
Expires: Mon, 02 Jun 2025 10:00:00 GMT
Cache-Control: max-age=3600
Cache-Control 优先级高于 Expires。两个都写是为了兼容老版本客户端。
四、强缓存命中时发生了什么

第三部分:协商缓存
协商缓存 = 缓存过期了(或设置了 no-cache),浏览器向服务器验证"我的缓存还能用吗?"
服务器根据资源的标识判断:
-
没变 → 返回
304 Not Modified,浏览器继续用缓存 -
变了 → 返回
200 OK+ 新内容
一、Last-Modified / If-Modified-Since(基于时间)

Last-Modified 的缺陷:
-
秒级精度不够:如果文件在 1 秒内被修改两次,Last-Modified 分辨不出
-
内容不变但修改时间变了:文件被重新保存(没改内容),Last-Modified 也变了
-
不适用于动态资源:动态生成的页面没有"最后修改时间"
二、ETag / If-None-Match(基于内容,推荐)

ETag 如何生成?
-
对文件内容做 MD5 或 SHA-1 哈希
-
Nginx 默认用文件修改时间 + 文件大小生成
-
动态资源可以用业务数据的版本号
ETag 解决了 Last-Modified 的所有缺陷:
| 问题 | Last-Modified | ETag |
|---|---|---|
| 秒级精度不够 | ❌ | ✅ 内容变了就变 |
| 只改时间不改内容 | ❌ 会误判 | ✅ 内容不变 ETag 不变 |
| 动态资源 | ❌ 没有修改时间 | ✅ 可自定义生成 |
三、Last-Modified 和 ETag 同时存在
响应头:
Last-Modified: Mon, 02 Jun 2024 10:00:00 GMT
ETag: "abc123"
ETag 优先级更高。浏览器先验证 ETag,再验证 Last-Modified。两个都写也是为了兼容性。
第四部分:浏览器行为
一、不同操作对缓存的影响
| 操作 | 强缓存 | 协商缓存 |
|---|---|---|
| 地址栏回车、链接跳转 | ✅ 生效 | ✅ 生效 |
| F5 刷新 | ❌ 跳过 | ✅ 生效 |
| Ctrl + F5 强制刷新 | ❌ 跳过 | ❌ 跳过 |

二、浏览器缓存位置

第五部分:缓存策略实战
一、不同类型的资源如何设置缓存
| 资源类型 | 推荐策略 | Cache-Control | 原因 |
|---|---|---|---|
| 首页 HTML | no-cache |
max-age=0, must-revalidate |
每次验证,确保最新 |
| CSS/JS(带版本号) | 强缓存 | max-age=31536000, immutable |
一年不过期 |
| 图片/字体 | 强缓存 | max-age=2592000 |
30 天 |
| API 接口 | no-store 或协商缓存 |
no-cache |
数据实时性高 |
| 用户头像 | 短强缓存 | max-age=300 |
5 分钟 |
二、为什么 CSS/JS 文件名要带哈希
<!-- 老方式(有缓存问题) -->
<link rel="stylesheet" href="/style.css">
<!-- 现代方式(带版本哈希) -->
<link rel="stylesheet" href="/style.a3f2b1c.css">

第六部分:完整示例
# 服务器响应头配置示例(Nginx)
# HTML:不缓存,每次验证
location / {
add_header Cache-Control "no-cache, must-revalidate";
}
# 带哈希的静态文件:长期缓存
location ~* \.[a-f0-9]{8}\.(css|js)$ {
add_header Cache-Control "max-age=31536000, immutable";
}
# 图片:中期缓存
location ~* \.(png|jpg|gif|svg|ico)$ {
add_header Cache-Control "max-age=2592000";
}
第七部分:面试题
1. Q:强缓存和协商缓存有什么区别?
A:强缓存命中时不向服务器发请求,直接用本地缓存(200 from cache)。协商缓存每次向服务器发请求验证,304 时用缓存,200 时重新下载。
2. Q:Expires 和 Cache-Control 的区别?
A:Expires 是 HTTP/1.0 的,用绝对时间(服务器时间),客户端时间不准会出问题。Cache-Control 是 HTTP/1.1 的,用相对时间(max-age),优先级更高。
3. Q:no-cache 和 no-store 的区别?
A:no-cache 可以缓存,但每次必须向服务器验证。no-store 完全不缓存。
4. Q:Last-Modified 和 ETag 的区别?
A:Last-Modified 基于时间,秒级精度,时间变了内容不变也会误判。ETag 基于内容哈希,更准确,优先级更高。
5. Q:F5 刷新和 Ctrl+F5 强制刷新的区别?
A:F5 跳过强缓存,走协商缓存(带上 Cache-Control: max-age=0)。Ctrl+F5 跳过所有缓存,重新下载(带上 Cache-Control: no-cache + Pragma: no-cache)。
6. Q:为什么静态资源文件名要带哈希?
A:配合强缓存使用。文件内容变了 → 哈希变了 → 文件名变了 → 浏览器认为是新文件 → 不走缓存直接下载。没变 → 文件名不变 → 走缓存。
总结
一、核心对比
| 对比 | 强缓存 | 协商缓存 |
|---|---|---|
| 发请求 | ❌ 不发 | ✅ 发 |
| 状态码 | 200 (from cache) | 304 |
| 控制字段 | Cache-Control / Expires | ETag / Last-Modified |
| 优先级 | 先判断 | 强缓存失效后才走 |
| 速度 | 最快(0ms 网络) | 较快(有请求但无响应体) |
二、一句话记忆
HTTP 缓存分两级:强缓存用 Cache-Control/Expires 控制有效期,有效期内根本不发请求;过期后用 ETag/Last-Modified 向服务器协商验证,304 就继续用旧的、200 就下载新的。no-cache 不是不缓存、no-store 才是不缓存。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)