深入理解 HTTP:从协议演进到报文细节

HTTP 是互联网的基石,从最初的简单文本传输协议,经历了三十年的演进,发展为支撑现代 Web 应用的复杂协议族。本文将深入 HTTP 报文结构、连接管理、协议演进、缓存机制等核心细节。


一、HTTP 报文结构

1.1 请求报文

HTTP 请求报文由四部分组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌─────────────────────────────────────────────┐
│ 请求行 │
│ 方法 URI HTTP版本 CRLF │
│ GET /index.html HTTP/1.1 │
├─────────────────────────────────────────────┤
│ 请求头(Header) │
│ Host: example.com │
│ User-Agent: Mozilla/5.0 │
│ Accept: text/html │
├─────────────────────────────────────────────┤
│ 空行(CRLF) │
├─────────────────────────────────────────────┤
│ 请求体(Body)—— GET 请求通常为空 │
│ {"username":"admin","password":"123"} │
└─────────────────────────────────────────────┘

1.2 响应报文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌─────────────────────────────────────────────┐
│ 状态行 │
│ HTTP版本 状态码 状态描述 CRLF │
│ HTTP/1.1 200 OK │
├─────────────────────────────────────────────┤
│ 响应头(Header) │
│ Content-Type: text/html; charset=utf-8 │
│ Content-Length: 1234 │
│ Cache-Control: max-age=3600 │
├─────────────────────────────────────────────┤
│ 空行(CRLF) │
├─────────────────────────────────────────────┤
│ 响应体(Body) │
│ <html>...</html> │
└─────────────────────────────────────────────┘

1.3 关键 Header 详解

请求头:

Header 说明 示例值
Host 目标主机(必填) example.com
User-Agent 客户端类型 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept 接受的响应格式 text/html, application/json
Accept-Language 偏好语言 zh-CN,zh;q=0.9,en;q=0.8
Accept-Encoding 支持的压缩 gzip, deflate, br
Authorization 认证凭证 Bearer eyJhbGciOiJIUzI1NiJ9...
Cookie 携带的 Cookie session=abc123; theme=dark
Content-Type 请求体格式 application/json
If-None-Match 协商缓存 ETag "33a64df5"
If-Modified-Since 协商缓存时间 Wed, 21 Oct 2026 07:28:00 GMT

响应头:

Header 说明 示例值
Content-Type 响应体格式 text/html; charset=utf-8
Content-Length 响应体字节数 1234
Content-Encoding 压缩方式 gzip
Cache-Control 缓存策略 no-cache, max-age=0
Set-Cookie 设置 Cookie session=abc123; HttpOnly; Secure
ETag 资源标识 "33a64df5"
Location 重定向目标 https://example.com/new
Access-Control-Allow-Origin CORS 允许 *
Strict-Transport-Security HSTS 策略 max-age=63072000

通用 Header(请求和响应均可出现):

Header 说明
Date 报文创建时间
Cache-Control 缓存控制
Connection 连接管理(keep-alive / close)
Via 代理路径

二、HTTP 连接管理

2.1 短连接 vs 长连接

1
2
3
4
5
6
7
8
9
10
11
短连接(HTTP/1.0 默认):
请求1 → 建立连接 → 传输 → 关闭
请求2 → 建立连接 → 传输 → 关闭
请求3 → 建立连接 → 传输 → 关闭
问题:每次请求都要三次握手,开销大

长连接(HTTP/1.1 默认,Keep-Alive):
请求1 → 建立连接 → 传输
请求2 → 复用连接 → 传输
请求3 → 复用连接 → 传输 → 关闭
优势:减少握手开销,提升性能

配置长连接:

1
2
3
4
5
# 请求头
Connection: keep-alive
Keep-Alive: timeout=5, max=100
# timeout=5:空闲 5 秒后关闭
# max=100:最多处理 100 个请求后关闭

2.2 队头阻塞(Head-of-Line Blocking)

1
2
3
4
5
6
7
8
9
10
HTTP/1.1 管道化:
客户端 服务器
| --- 请求1 -----------> |
| --- 请求2 -----------> | ← 必须等请求1响完才能发请求3
| --- 请求3 -----------> |
| <--- 响应1 ----------- |
| <--- 响应2 ----------- | ← 响应必须按请求顺序返回
| <--- 响应3 ----------- |

问题:请求2即使先处理完,也必须等请求1的响应发完

这被称为队头阻塞,是 HTTP/1.1 的核心性能瓶颈。

2.3 浏览器的应对策略

策略 说明
多连接并发 同一域名建立 6-8 个 TCP 连接
域名分片 将资源分散到多个子域名(a.example.com, b.example.com)
资源合并 小文件合并为大文件(雪碧图、JS 合并)
嵌入资源 小文件内联为 Base64(data URI)

三、HTTP 协议演进

3.1 版本对比

特性 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/3
连接方式 短连接 长连接 多路复用 多路复用
传输格式 文本 文本 二进制帧 二进制帧
头部压缩 HPACK QPACK
服务器推送 支持 支持
队头阻塞 严重 有(TCP 层) 有(TCP 层) 无(基于 UDP)
建连延迟 1-RTT 1-RTT 1-RTT + TLS 0-RTT
底层协议 TCP TCP TCP QUIC (UDP)
发布年份 1996 1997 2015 2022

3.2 HTTP/1.1 改进

相比 HTTP/1.0 的主要改进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 持久连接(Keep-Alive)
→ 默认开启,减少 TCP 握手

2. 分块传输编码(Chunked Transfer)
→ 不需要 Content-Length,边生成边发送
→ 响应头:Transfer-Encoding: chunked

3. Host 头必填
→ 支持虚拟主机,一个 IP 多个域名

4. 新增请求方法
→ PUT、DELETE、OPTIONS、TRACE、CONNECT

5. 缓存控制
→ 引入 Cache-Control 替代简单的 Expires

3.3 HTTP/2 核心特性

二进制分帧层:

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1(文本):
GET /index.html HTTP/1.1\r\nHost: example.com\r\n\r\n
→ 一行一行的文本,解析效率低

HTTP/2(二进制帧):
┌──────────┬──────────┬──────────┬──────────┐
│ Length │ Type │ Flags │ Stream ID│
│ 3 bytes │ 1 byte │ 1 byte │ 4 bytes │
├──────────┴──────────┴──────────┴──────────┤
│ Payload (HPACK 编码的头部或数据) │
└───────────────────────────────────────────┘
→ 固定格式,解析高效

多路复用(Multiplexing):

1
2
3
4
5
6
7
8
9
10
HTTP/1.1(串行):
请求1: ████████████
请求2: ████████████
请求3: ████████████

HTTP/2(并行):
请求1: ████ ████ ████ ████
请求2: ████ ████ ████
请求3: ████ ████ ████
→ 交错发送,互不阻塞

头部压缩(HPACK):

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 每次请求都发送完整头部:
GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 ...
Accept: text/html
Accept-Language: zh-CN
Cookie: session=abc123
→ 重复内容多,浪费带宽

HPACK 压缩:
用静态表 + 动态表 + 霍夫曼编码
→ 常见头部用索引代替,大幅减少传输量

服务器推送(Server Push):

1
2
3
4
5
6
客户端请求 index.html
服务器主动推送:
→ index.html
→ style.css(预测客户端需要)
→ app.js(预测客户端需要)
→ 减少请求往返

3.4 HTTP/3 与 QUIC

HTTP/3 的核心是 QUIC 协议(基于 UDP),解决了 TCP 层的队头阻塞:

1
2
3
4
5
6
7
8
9
TCP 队头阻塞:
流1: ████████ ████ ████████
流2: ████ ← 丢包! ████ ← 流2也要等流1重传完成
流3: ████ ← 被阻塞! ████

QUIC 无队头阻塞:
流1: ████████ ████ ████████
流2: ████ ← 丢包!只重传流2 ████ ← 流1、流3不受影响
流3: ████ ████ ████

QUIC 的关键改进:

特性 TCP + TLS QUIC
建连延迟 TCP 1-RTT + TLS 1-RTT = 2-RTT 1-RTT(首次),0-RTT(恢复)
队头阻塞 TCP 层存在
连接迁移 IP+端口变化需重连 Connection ID,网络切换无感
拥塞控制 内核实现,更新慢 用户态实现,可快速迭代

四、内容协商与编码

4.1 内容协商机制

内容协商让客户端和服务器就能返回的资源格式达成一致:

1
2
3
4
5
6
7
8
9
10
客户端发送偏好:
Accept: text/html, application/json;q=0.9, */*;q=0.8
→ q 值表示优先级(0-1),默认 1.0

Accept-Language: zh-CN;q=1.0, zh;q=0.9, en;q=0.8
→ 优先返回中文

服务器决定:
Content-Type: application/json
Content-Language: zh-CN

协商类型:

类型 方式 示例
服务端驱动 服务器根据 Accept 头决定 最常用
客户端驱动 返回多个版本,客户端选择 内容协商 API
透明协商 代理服务器根据缓存决定 较少使用

4.2 压缩编码

算法 压缩率 速度 CPU 开销 使用场景
gzip 最通用
deflate 较老的客户端
br (Brotli) 最高 现代浏览器,静态资源
zstd 最快 新兴,Chrome 支持
1
2
3
4
5
# 客户端支持的压缩
Accept-Encoding: gzip, deflate, br

# 服务器选择的压缩
Content-Encoding: br

4.3 分块传输编码

当响应体大小未知时(如动态生成内容),使用分块传输:

1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/plain

1a\r\n
这是第一块内容(26字节)\r\n
15\r\n
这是第二块内容(21字节)\r\n
0\r\n
\r\n

每个块的格式:块大小\r\n块内容\r\n,最后一块大小为 0。


五、HTTP 缓存机制

5.1 强缓存

强缓存直接使用本地缓存,不向服务器发起请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─────────────────────────────────────────────┐
│ Cache-Control: max-age=3600 │
│ → 资源在 3600 秒内有效 │
│ → 优先级高于 Expires │
│ │
│ Cache-Control: no-cache │
│ → 跳过强缓存,每次都走协商缓存 │
│ │
│ Cache-Control: no-store │
│ → 完全不缓存 │
│ │
│ Expires: Thu, 01 Dec 2026 16:00:00 GMT │
│ → 绝对时间(已过时,受时钟影响) │
└─────────────────────────────────────────────┘

5.2 协商缓存

强缓存失效后,携带标识向服务器验证:

1
2
3
4
5
6
7
8
9
方案一:Last-Modified / If-Modified-Since
响应:Last-Modified: Wed, 21 Oct 2026 07:28:00 GMT
请求:If-Modified-Since: Wed, 21 Oct 2026 07:28:00 GMT
→ 服务器比较时间,未修改返回 304

方案二:ETag / If-None-Match(推荐)
响应:ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
请求:If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
→ 服务器比较内容哈希,未修改返回 304

ETag vs Last-Modified:

特性 ETag Last-Modified
精确度 字节级 秒级
可靠性 高(基于内容) 低(基于修改时间)
性能 需要计算哈希 读取文件时间即可
分布式 多机可能不一致 一致

5.3 缓存决策流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
请求到达

├─ 是否有缓存? → 无 → 发起请求

├─ 有缓存
│ ├─ Cache-Control: no-store? → 是 → 发起请求
│ │
│ ├─ 强缓存是否过期?(max-age/Expires)
│ │ ├─ 未过期 → 直接使用缓存(200 from cache)
│ │ └─ 已过期 → 进入协商缓存
│ │
│ └─ 协商缓存
│ ├─ 发送 If-None-Match / If-Modified-Since
│ ├─ 服务器返回 304 → 使用缓存
│ └─ 服务器返回 200 → 使用新资源

5.4 缓存最佳实践

资源类型 推荐策略
HTML 文件 Cache-Control: no-cache(每次验证)
CSS/JS(带 hash) Cache-Control: max-age=31536000, immutable
图片(带 hash) Cache-Control: max-age=31536000
API 响应 Cache-Control: no-store(不缓存)
字体文件 Cache-Control: max-age=31536000, immutable

六、断点续传与范围请求

6.1 Range 请求

1
2
3
4
5
6
7
8
9
# 客户端请求指定范围
GET /large-file.zip HTTP/1.1
Range: bytes=0-499
→ 请求前 500 字节

# 服务器响应
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-499/123456
Content-Length: 500

6.2 多段范围请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 客户端请求多个范围
Range: bytes=0-499, 1000-1499, 2000-2499

# 服务器响应(multipart/byteranges)
HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=boundary

--boundary
Content-Range: bytes 0-499/123456
(第一段内容)
--boundary
Content-Range: bytes 1000-1499/123456
(第二段内容)
--boundary--

6.3 断点续传实现

1
2
3
4
5
6
7
8
9
10
11
12
客户端                        服务器
| GET /file.zip |
| Range: bytes=0- |
| <--- 206 Content --------- |
| (下载中...) |
| 连接断开!已下载 50% |
| |
| 重新连接 |
| GET /file.zip |
| Range: bytes=500000- |
| <--- 206 Content --------- |
| (继续下载剩余部分) |

七、HTTP 代理与隧道

7.1 正向代理 vs 反向代理

特性 正向代理 反向代理
代理对象 客户端 服务器
客户端感知 知道代理存在 不知道代理存在
典型用途 VPN、翻墙、缓存 负载均衡、SSL 终止、WAF
配置位置 客户端配置 服务器端部署
示例 Squid、Shadowsocks Nginx、HAProxy、Cloudflare

7.2 代理相关 Header

1
2
3
4
5
6
7
8
9
10
11
# Via:记录代理路径
Via: 1.1 proxy1.example.com, 1.1 proxy2.example.com

# X-Forwarded-For:原始客户端 IP
X-Forwarded-For: 203.0.113.195, 70.41.3.18

# X-Real-IP:真实客户端 IP(通常由反向代理设置)
X-Real-IP: 203.0.113.195

# X-Forwarded-Proto:原始协议
X-Forwarded-Proto: https

7.3 HTTPS 隧道(CONNECT 方法)

代理服务器通过 CONNECT 方法建立 TCP 隧道,转发加密流量:

1
2
3
4
5
6
7
客户端              代理服务器              目标服务器
| CONNECT example.com:443 --> |
| <--- 200 Connection Established |
| |
| === TLS 握手(端到端)====> |
| === 加密数据(透传)======> |
| |

总结

HTTP 从 1991 年的简单文本协议,演进到今天的 HTTP/3 与 QUIC,核心演进方向是:减少延迟、提升并发、增强安全

关键要点:

  • 报文结构:理解请求行/状态行、Header、Body 的组织方式
  • 连接管理:长连接减少握手开销,多路复用解决队头阻塞
  • 协议演进:HTTP/2 用二进制帧和多路复用提升性能,HTTP/3 用 QUIC 彻底解决 TCP 队头阻塞
  • 缓存机制:强缓存 + 协商缓存的分层策略,合理配置提升用户体验
  • 代理与隧道:正向代理保护客户端,反向代理保护服务器

掌握这些细节,能帮助你在开发和运维中更好地理解 Web 通信,做出更合理的技术决策。

🔥 0 打卡天