深入理解 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 打卡天