HTTP 协议全解 — Web 世界的通用语言
HTTP 是互联网上最广泛使用的应用层协议。从你打开网页到刷短视频,从在线支付到 API 调用,HTTP 几乎无处不在。它已经从一个简单的文本传输协议,演化成了支撑整个现代 Web 生态的核心基础设施。
📋 开篇自测:你已经知道多少?
- HTTP/1.1 的 Keep-Alive 长连接和 HTTP/2 的多路复用有什么区别?
- 你能说出至少 8 个 HTTP 状态码及其含义吗?
- HTTP 缓存中的 ETag 和 Last-Modified 分别是如何工作的?
一、HTTP 的演进之路
1.1 从 0.9 到 3:三十年的进化
HTTP 版本时间线:
1991 HTTP/0.9 只支持 GET,只能传 HTML,无头部
|
1996 HTTP/1.0 增加 POST/HEAD,引入头部、状态码、内容类型
|
1999 HTTP/1.1 持久连接、管道化、分块传输、Host头 <-- 统治20年
|
2015 HTTP/2 二进制帧、多路复用、头部压缩、服务器推送
|
2022 HTTP/3 基于 QUIC(UDP),解决队头阻塞,0-RTT 连接
1.2 HTTP/1.0 到 1.1 的关键改进
HTTP/1.0 每次请求都要重新建立 TCP 连接,效率极低。HTTP/1.1 做出了重要改进:
HTTP/1.0 (短连接):
TCP握手 --> GET /index.html --> 响应 --> TCP挥手
TCP握手 --> GET /style.css --> 响应 --> TCP挥手
TCP握手 --> GET /logo.png --> 响应 --> TCP挥手
(3次TCP连接建立和关闭!)
HTTP/1.1 (持久连接 Keep-Alive):
TCP握手 --> GET /index.html --> 响应
--> GET /style.css --> 响应
--> GET /logo.png --> 响应 --> TCP挥手
(只需1次TCP连接!)
HTTP/1.1 还引入了:
- 管道化(Pipelining):客户端可以不等前一个响应就发送下一个请求(但服务端仍需按序响应,因此存在队头阻塞问题,实践中几乎未被主流浏览器启用)
- 分块传输(Chunked Transfer):允许服务端在不知道总大小时逐块发送数据
- Host 头部:允许一个 IP 地址托管多个域名(虚拟主机)
二、HTTP 请求与响应
2.1 请求报文格式
POST /api/users HTTP/1.1 <-- 请求行: 方法 + URI + 版本
Host: api.example.com <-- 头部字段开始
Content-Type: application/json
Authorization: Bearer eyJhb...
Content-Length: 42
Accept: application/json
<-- 空行(头部结束)
{"name": "Alice", "age": 30} <-- 请求体
2.2 响应报文格式
HTTP/1.1 201 Created <-- 状态行: 版本 + 状态码 + 原因短语
Content-Type: application/json
Content-Length: 65
Date: Wed, 18 Mar 2026 10:30:00 GMT
Cache-Control: no-cache
<-- 空行
{"id": 1001, "name": "Alice"} <-- 响应体
2.3 HTTP 请求方法
| 方法 | 语义 | 幂等 | 安全 | 常见用途 |
|---|---|---|---|---|
| GET | 获取资源 | 是 | 是 | 查询数据、加载页面 |
| POST | 创建资源/提交数据 | 否 | 否 | 表单提交、创建记录 |
| PUT | 完整替换资源 | 是 | 否 | 更新整个资源 |
| PATCH | 部分更新资源 | 否 | 否 | 更新资源的部分字段 |
| DELETE | 删除资源 | 是 | 否 | 删除记录 |
| HEAD | 同 GET 但只返回头部 | 是 | 是 | 检查资源是否存在 |
| OPTIONS | 查询支持的方法 | 是 | 是 | CORS 预检请求 |
幂等:多次执行同一操作,结果与执行一次相同。 安全:不会修改服务器资源。
三、HTTP 状态码
3.1 五大类别
状态码分类:
1xx 信息性响应 -- "你的请求我收到了,先等等"
2xx 成功 -- "你的请求处理成功了"
3xx 重定向 -- "你找的东西不在这里,去那边看看"
4xx 客户端错误 -- "你的请求有问题"
5xx 服务端错误 -- "我这边出问题了"
3.2 常用状态码详解
200 OK 请求成功,返回数据
201 Created 资源创建成功(POST 常用)
204 No Content 成功但无返回体(DELETE 常用)
301 Moved Permanently 资源永久移动(搜索引擎会更新索引)
302 Found 资源临时移动(浏览器不缓存新地址)
304 Not Modified 资源未修改,使用缓存(配合条件请求)
400 Bad Request 请求格式错误
401 Unauthorized 未认证(需要登录)
403 Forbidden 已认证但无权限(权限不足)
404 Not Found 资源不存在
405 Method Not Allowed 请求方法不支持
409 Conflict 资源冲突(如重复创建)
429 Too Many Requests 请求频率过高(限流)
500 Internal Server Error 服务器内部错误
502 Bad Gateway 网关收到无效响应
503 Service Unavailable 服务暂不可用(过载或维护)
504 Gateway Timeout 网关超时
3.3 面试常考:301 vs 302
301 Moved Permanently:
浏览器: GET http://old.com/page
服务器: 301, Location: https://new.com/page
浏览器: 缓存这个重定向,下次直接访问 https://new.com/page
搜索引擎: 更新索引,把权重转移到新URL
场景: 网站域名变更、HTTP升级到HTTPS
302 Found:
浏览器: GET http://example.com/login
服务器: 302, Location: http://example.com/dashboard
浏览器: 本次跳转,但不缓存,下次还是先访问原URL
搜索引擎: 保留原URL的索引
场景: 登录后跳转、A/B测试、临时维护页
四、HTTP 头部详解
4.1 通用头部
Date: Wed, 18 Mar 2026 10:30:00 GMT -- 消息产生的时间
Connection: keep-alive -- 连接管理
Transfer-Encoding: chunked -- 传输编码方式
Cache-Control: max-age=3600 -- 缓存控制
4.2 请求头部
Host: www.example.com -- 目标主机(HTTP/1.1 必须)
User-Agent: Mozilla/5.0 ... -- 客户端标识
Accept: text/html, application/json -- 可接受的内容类型
Accept-Encoding: gzip, deflate, br -- 可接受的压缩方式
Accept-Language: zh-CN,en;q=0.9 -- 偏好的语言
Authorization: Bearer eyJhb... -- 认证凭据
Cookie: session_id=abc123 -- Cookie
Referer: https://www.google.com/ -- 来源页面
If-None-Match: "etag-value" -- 条件请求(ETag)
If-Modified-Since: Wed, 18 Mar 2026... -- 条件请求(修改时间)
4.3 响应头部
Content-Type: application/json; charset=utf-8 -- 内容类型和编码
Content-Length: 1234 -- 内容长度
Content-Encoding: gzip -- 内容压缩方式
Set-Cookie: session_id=abc123; HttpOnly -- 设置Cookie
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4" -- 资源指纹
Last-Modified: Wed, 18 Mar 2026 08:00:00 GMT -- 最后修改时间
Access-Control-Allow-Origin: * -- CORS
Location: https://new.example.com/ -- 重定向目标
五、HTTP 缓存机制
5.1 缓存的重要性
HTTP 缓存可以显著减少网络请求、降低服务器负载、加快页面加载速度。理解缓存机制是 Web 性能优化的关键。
5.2 强缓存
强缓存命中时,浏览器直接使用本地缓存,不发送任何请求到服务器:
首次请求:
浏览器 --> GET /logo.png --> 服务器
浏览器 <-- 200 OK, Cache-Control: max-age=86400 <-- 服务器
(缓存1天)
第二次请求(1天内):
浏览器 --> 检查本地缓存 --> 未过期 --> 直接使用缓存 (状态: 200 from cache)
(不发送任何网络请求!)
Cache-Control 指令:
max-age=3600 缓存有效期3600秒
no-cache 每次使用前必须验证(不是不缓存!)
no-store 真正的不缓存,每次都重新请求
public 任何中间节点都可以缓存
private 只有浏览器可以缓存(默认)
must-revalidate 过期后必须验证,不能用陈旧缓存
5.3 协商缓存
当强缓存过期后,浏览器会向服务器发送条件请求来验证资源是否变化:
基于 ETag 的协商缓存:
首次请求:
浏览器 --> GET /data.json --> 服务器
浏览器 <-- 200, ETag: "abc123" <-- 服务器
缓存过期后再次请求:
浏览器 --> GET /data.json
If-None-Match: "abc123" --> 服务器
情况A: 资源未变化
浏览器 <-- 304 Not Modified (无响应体) <-- 服务器
浏览器使用本地缓存,节省带宽!
情况B: 资源已变化
浏览器 <-- 200, ETag: "def456", 新数据 <-- 服务器
基于 Last-Modified 的协商缓存:
首次请求:
浏览器 <-- 200, Last-Modified: Wed, 18 Mar 2026 08:00:00 GMT
再次请求:
浏览器 --> If-Modified-Since: Wed, 18 Mar 2026 08:00:00 GMT
服务器比较时间 --> 304 或 200
5.4 缓存决策流程
浏览器发起请求
|
v
是否有本地缓存? --否--> 发送请求获取资源
|
是
|
v
强缓存是否过期? --否--> 直接使用缓存 (200 from cache)
|
是
|
v
有 ETag/Last-Modified? --否--> 发送普通请求
|
是
|
v
发送条件请求 (If-None-Match / If-Modified-Since)
|
v
服务器验证
|
+---+---+
| |
未变 已变
| |
v v
304 200
用缓存 用新数据
六、Cookie 与会话管理
6.1 Cookie 的工作原理
HTTP 是无状态协议,Cookie 是在无状态之上实现状态管理的机制:
首次访问:
浏览器 --> GET /login (用户名+密码) --> 服务器
浏览器 <-- 200, Set-Cookie: session_id=xyz789; Path=/; HttpOnly <-- 服务器
后续请求(浏览器自动带上Cookie):
浏览器 --> GET /profile, Cookie: session_id=xyz789 --> 服务器
服务器根据 session_id 识别用户
6.2 Cookie 的属性
Set-Cookie: session_id=xyz789;
Domain=example.com; -- 作用域(含子域名)
Path=/; -- 路径范围
Expires=Fri, 20 Mar 2026 12:00:00 GMT; -- 过期时间
Max-Age=86400; -- 有效期(秒),优先于Expires
Secure; -- 只通过HTTPS传输
HttpOnly; -- JavaScript 不可访问(防XSS)
SameSite=Lax; -- 限制跨站发送(防CSRF)
七、HTTP/2:性能飞跃
7.1 HTTP/1.1 的瓶颈
HTTP/1.1 的队头阻塞(Head-of-Line Blocking):
请求1(大文件) |-----------处理中-----------|
请求2(小文件) |--处理--|
请求3(小文件) |--处理--|
请求2和3必须等待请求1完成,即使它们很快就能处理完
为了绕过这个限制,浏览器通常会对同一域名开启 6 个并发 TCP 连接。但这带来了更多的 TCP 握手开销和拥塞控制竞争。
7.2 HTTP/2 的核心特性
二进制帧:HTTP/2 不再是纯文本协议,所有数据被分割成二进制帧传输,解析效率更高。
多路复用:在一个 TCP 连接上同时传输多个请求和响应,彻底解决应用层队头阻塞:
HTTP/1.1 (6个TCP连接,每个串行):
TCP-1: [req1] --> [resp1]
TCP-2: [req2] --> [resp2]
TCP-3: [req3] --> [resp3]
...
HTTP/2 (1个TCP连接,多路复用):
TCP: [帧:req1][帧:req2][帧:req3][帧:resp2][帧:resp1][帧:resp3]
所有请求和响应交织在同一连接上,互不阻塞
头部压缩(HPACK):使用静态表和动态表压缩重复的头部字段:
HTTP/1.1 每次请求都发送完整头部:
GET /page1 Host: example.com User-Agent: ... Accept: ... (约500字节)
GET /page2 Host: example.com User-Agent: ... Accept: ... (约500字节)
重复!
HTTP/2 HPACK 压缩:
GET /page1 完整头部发送一次
GET /page2 只发送差异部分 (约20字节)
服务器推送(已被主流浏览器弃用):HTTP/2 规范中定义了 Server Push,允许服务器预判客户端需要的资源并主动推送。但由于实际效果不佳(缓存利用率低、带宽浪费等问题),Chrome 106(2022 年)率先移除了对 Server Push 的支持,随后其他主流浏览器也跟进弃用。
目前推荐的替代方案是 103 Early Hints:
103 Early Hints 工作流程:
浏览器: GET /index.html
服务器: 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
(浏览器收到提示后立即开始请求这些资源)
服务器: 200 OK (返回完整的 index.html)
浏览器: CSS 和 JS 已经在路上了,页面加载更快!
八、HTTP/3:基于 QUIC 的下一代
8.1 HTTP/2 的遗留问题
HTTP/2 解决了应用层队头阻塞,但 TCP 层的队头阻塞依然存在:
TCP 层队头阻塞:
HTTP/2 的多路复用在 TCP 之上
Stream1: [帧1][帧2] Stream2: [帧3][帧4]
| |
v v
TCP: [帧1][帧2][帧3][帧4] --> 字节流
如果 帧2 的 TCP 包丢失:
TCP 要求按序交付 --> 帧3、帧4 虽然已到达,也必须等待帧2重传
Stream2 被 Stream1 的丢包拖累!
8.2 QUIC 如何解决
QUIC 基于 UDP,在应用层自行实现了可靠传输,每个流独立控制:
QUIC 的独立流:
Stream1: [帧1][ 帧2(丢失,重传) ] --> 不影响其他流
Stream2: [帧3][帧4] --> 正常交付!
其他优势:
1. 0-RTT 连接建立(已知服务器时无需握手即可发数据)
2. 连接迁移(IP变化时不断开连接,如Wi-Fi切4G)
3. 内置TLS 1.3加密
8.3 版本对比总结
特性对比:
+------------------+----------+----------+----------+
| 特性 | HTTP/1.1 | HTTP/2 | HTTP/3 |
+------------------+----------+----------+----------+
| 传输层 | TCP | TCP | QUIC/UDP |
| 格式 | 文本 | 二进制 | 二进制 |
| 多路复用 | 否 | 是 | 是 |
| 头部压缩 | 否 | HPACK | QPACK |
| 服务器推送 | 否 | 规范支持,浏览器已弃用 | 规范支持,浏览器已弃用 |
| TCP队头阻塞 | 有 | 有 | 无 |
| 0-RTT | 否 | 否 | 是 |
| 连接迁移 | 否 | 否 | 是 |
+------------------+----------+----------+----------+
🤔 想一想 既然 HTTP/3 这么好,为什么 HTTP/1.1 至今仍是最广泛使用的版本?升级到 HTTP/2 和 HTTP/3 有哪些实际障碍?
九、章节小结
- HTTP 从 0.9 演进到 3.0,核心驱动力是对性能、效率和安全的不断追求。
- 请求和响应由请求行/状态行、头部和可选的请求体组成,格式清晰明确。
- 状态码分五类,2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务端错误。
- 缓存机制分为强缓存和协商缓存两级,正确配置可以大幅提升 Web 性能。
- HTTP/2 通过多路复用、头部压缩和服务器推送大幅提升了传输效率。
- HTTP/3 基于 QUIC 协议解决了 TCP 层队头阻塞问题,支持 0-RTT 连接和连接迁移。
📝 结尾自测:检验你的收获
- HTTP/1.1 的 Keep-Alive 和 HTTP/2 的多路复用有什么本质区别?为什么后者能解决队头阻塞?
- 状态码 301 和 302 的区别是什么?它们对浏览器行为和搜索引擎优化有什么不同影响?
- 描述 HTTP 缓存的完整判断流程:浏览器如何决定使用缓存还是重新请求?
- Cookie 的 HttpOnly 和 SameSite 属性分别防御什么安全威胁?
- HTTP/3 为什么选择 UDP 而非 TCP 作为传输层?QUIC 协议解决了 HTTP/2 的什么问题?
下一章预告:HTTP 以明文传输数据,在开放的网络中面临窃听、篡改和冒充三大风险。下一章我们将深入 HTTPS 与网络安全——理解 TLS 握手的每一步、数字证书如何建立信任链,以及 XSS、CSRF、中间人攻击等常见威胁的攻防手法。
购买课程解锁全部内容
网络通信第一课:10 章掌握计算机网络
¥29.90