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> │ └─────────────────────────────────────────────┘
|
请求头:
| 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
Content-Range: bytes 0-499/123456 (第一段内容)
Content-Range: bytes 1000-1499/123456 (第二段内容)
|
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 |
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 通信,做出更合理的技术决策。