HTTP协议演进-1-2-3
1. 概念与原理
HTTP 是前端的母语,但 90% 前端只熟 HTTP/1.1 的语义层(method、status、header),对底层传输(多路复用、流控、队头阻塞)一知半解。这一篇把 HTTP/1.0 到 HTTP/3 的演进彻底讲透,让你能判断什么时候该开 HTTP/2、什么时候 HTTP/3 真有用、为什么 HTTP/2 没杀掉合并请求。
2. HTTP/0.9 到 HTTP/1.0
略过历史,记几个关键点:
- HTTP/0.9(1991):只有 GET,没 header,纯文本
- HTTP/1.0(1996):加了 header、method、status,每请求一个 TCP 连接
HTTP/1.0 致命缺陷:每个请求都要三次握手 + 慢启动,加载 10 张图 = 10 次握手。
3. HTTP/1.1(1997,至今主流)
3.1 持久连接 keep-alive
Connection: keep-alive
Keep-Alive: timeout=60, max=100
一个 TCP 连接发多个请求,省握手开销。HTTP/1.1 默认开启。
3.2 管道化(Pipelining)— 失败的尝试
允许客户端不等响应连续发请求,服务端按顺序返回。但队头阻塞问题严重:第一个请求慢,后面全等。浏览器全部默认关闭,实际等于没用。
3.3 队头阻塞(HoL,Head-of-Line Blocking)
HTTP/1.1 的根本问题:一个 TCP 连接上请求必须串行。慢请求拖慢所有后续。
浏览器的妥协:同一域名最多 6 个并行连接。所以才有"域名分片"(cdn1.x.com, cdn2.x.com)技巧。
3.4 分块传输 Transfer-Encoding: chunked
服务端不知道总长度时(流式生成、SSR),分块发送:
HTTP/1.1 200 OK
Transfer-Encoding: chunked
7\r\n
Mozilla\r\n
9\r\n
Developer\r\n
0\r\n
\r\n
每块 = 长度(hex)+ 内容。0\r\n\r\n 表示结束。Server-Sent Events、Streaming SSR 用这个。
3.5 Range / 断点续传
GET /video.mp4 HTTP/1.1
Range: bytes=1024-2047
HTTP/1.1 206 Partial Content
Content-Range: bytes 1024-2047/10485760
Content-Length: 1024
视频拖动进度条、下载断点续传、上传分片都靠这个。
3.6 缓存与协商缓存
| 头 | 类型 | 含义 |
|---|---|---|
Cache-Control: max-age=3600 | 强缓存 | 客户端缓存 3600 秒,不发请求 |
Cache-Control: no-cache | 协商缓存 | 必须发请求验证 |
Cache-Control: no-store | 不缓存 | 完全不存 |
Cache-Control: private/public | 范围 | private 只浏览器、public 可被 CDN |
Expires | 强缓存 | HTTP/1.0 老语法 |
ETag / If-None-Match | 协商 | 内容指纹 |
Last-Modified / If-Modified-Since | 协商 | 时间戳(精度只到秒) |
详见模块 09。
3.7 Connection: close vs keep-alive
Connection: close # 响应后立即关 TCP
Connection: keep-alive # 保持连接复用
HTTP/1.1 默认 keep-alive,但服务端可以主动 close(防止连接占用)。
4. HTTP/2(2015,RFC 7540)
4.1 关键改进
| 特性 | HTTP/1.1 | HTTP/2 |
|---|---|---|
| 传输格式 | 文本 | 二进制(帧) |
| 多路复用 | 否(HoL) | 是(一个 TCP 多个流并发) |
| Header 压缩 | 否 | HPACK |
| 服务端推送 | 否 | Server Push(已 deprecated) |
| 优先级 | 否 | 流优先级 |
4.2 二进制分帧
HTTP/2 把消息切成帧(frame),每帧带 stream ID。同一连接上多个 stream 的帧交错传输,接收方按 stream ID 重组。
连接 = TCP 连接
├── Stream 1(请求 1)
│ ├── HEADERS 帧
│ └── DATA 帧(多个)
├── Stream 3(请求 2,奇数客户端发起)
│ ├── HEADERS 帧
│ └── DATA 帧
└── Stream 5(请求 3)
抓包看:tcpdump + Wireshark 选 HTTP/2 协议解析,能看到 stream 交错。
4.3 多路复用解决了什么
一个域名一个 TCP 连接,承载所有请求。再也不用域名分片。
实测对比:100 个小资源加载:
- HTTP/1.1:6 连接,每连接串行,约 100 / 6 个串行批次
- HTTP/2:1 连接,全部并发
但域名分片在 HTTP/2 时代有害:每个域名一个 TCP + TLS 握手,反而慢。需要把资源合并到一个域名。
4.4 HPACK 头压缩
HTTP/1.1 每个请求重发 cookie、UA、Accept 这些头(几 KB)。HPACK:
- 静态表:常用头预定义编号(如
:method GET= 索引 2) - 动态表:连接内出现过的头存表,后续用索引引用
- Huffman 编码:剩余字符串再压
实测压缩率 70-90%。但 HPACK 是有状态的(依赖动态表同步),不能跨连接复用,QUIC 因此换成 QPACK。
4.5 流优先级
客户端可以告诉服务端 stream 优先级(权重 + 依赖树)。浏览器据此让 HTML > CSS > JS > 图片优先返回。
但实际服务端实现差,Chrome 后来改用更简单的 Priority Hints(fetchpriority="high")。
4.6 Server Push(已废)
服务端主动推送资源(HTML 还没解析就推 CSS)。理论上省一个 RTT。但:
- 浏览器实现复杂,缓存判断容易出错
- 不知道客户端是否已有资源
- Chrome 2022 移除支持
替代方案:HTTP 早期提示 103 Early Hints + Link rel=preload header。
4.7 TCP 队头阻塞依然存在
HTTP/2 解决了 HTTP 层 的队头阻塞,但 TCP 层的没解决:TCP 必须保证字节流有序,一个包丢失,后续所有 stream 都要等重传。弱网下 HTTP/2 可能比 HTTP/1.1 多连接更慢。
这就是 HTTP/3 出现的根本原因。
4.8 启用 HTTP/2
Nginx:
server {
listen 443 ssl http2;
...
}
HTTP/2 实际部署强制要求 HTTPS(RFC 没规定,但浏览器都要 TLS)。
5. HTTP/3(2022,RFC 9114)
5.1 基于 QUIC
QUIC 是 Google 设计、IETF 标准化的传输层协议,基于 UDP。HTTP/3 就是 HTTP over QUIC。
| 层级 | HTTP/2 | HTTP/3 |
|---|---|---|
| 应用 | HTTP/2 | HTTP/3 |
| 安全 | TLS 1.2/1.3 | TLS 1.3(内置) |
| 传输 | TCP | QUIC |
| 网络 | IP | IP(UDP) |
5.2 QUIC 的核心特性
真正消灭队头阻塞
QUIC 的 stream 独立可靠传输,一个 stream 丢包不影响别的 stream。HTTP/3 的多路复用是真的并行。
0-RTT 重连
首次连接:1-RTT 握手(TLS 1.3 + 传输参数)。复用 session:0-RTT,第一个包就带应用数据。
连接迁移
QUIC 用 Connection ID 标识连接,不依赖 IP+端口。手机从 WiFi 切 4G 时连接不断。
内置 TLS 1.3
不能用旧版 TLS,安全基线更高。
5.3 部署 HTTP/3
Nginx(1.25+ 支持):
server {
listen 443 ssl;
listen 443 quic reuseport; # UDP 443
http2 on;
http3 on;
add_header Alt-Svc 'h3=":443"; ma=86400'; # 通告 HTTP/3 可用
...
}
防火墙必须放行 UDP 443(很多公司只放 TCP 443)。
5.4 浏览器兼容
Chrome、Firefox、Edge、Safari 都已支持。首次访问还是 HTTP/2(不知道服务端支持 HTTP/3),看到 Alt-Svc 响应头后续请求切到 HTTP/3。
5.5 调试 HTTP/3
# curl 必须编译时开 HTTP/3 支持
curl --http3 -v https://cloudflare.com
# Chrome 看
# DevTools → Network → 右键列头加 "Protocol"
# h3 = HTTP/3, h2 = HTTP/2, http/1.1 = HTTP/1.1
6. 各版本性能对比
实际场景(首屏加载、100 个资源):
| 协议 | RTT | 队头阻塞 | 弱网表现 | 部署难度 |
|---|---|---|---|---|
| HTTP/1.1 | 高(多连接) | 严重 | 中(多连接对冲) | 低 |
| HTTP/2 | 中 | TCP 层有 | 弱网比 1.1 差 | 中(要 TLS) |
| HTTP/3 | 低 | 无 | 显著好 | 高(UDP、新栈) |
前端选型:
- 普通网站:HTTP/2 已足够
- 移动端 / 海外 / 弱网用户多:HTTP/3 有显著收益
- 流媒体 / 实时性敏感:HTTP/3
7. 关键头字段(前端必知)
7.1 请求头
| 头 | 用途 |
|---|---|
Host | 必填,虚拟主机分发 |
User-Agent | 浏览器标识 |
Accept、Accept-Language、Accept-Encoding | 内容协商 |
Authorization | 认证 |
Cookie | session |
Origin | CORS 源 |
Referer | 来源页(拼写 typo,已成历史) |
If-None-Match、If-Modified-Since | 协商缓存 |
Range | 断点续传 |
7.2 响应头
| 头 | 用途 |
|---|---|
Content-Type | 响应 MIME |
Content-Length | body 长度 |
Content-Encoding | gzip / br |
Cache-Control、ETag、Last-Modified | 缓存 |
Set-Cookie | 写 cookie |
Access-Control-* | CORS |
Strict-Transport-Security | HSTS |
Content-Security-Policy | CSP |
X-Frame-Options | 防 clickjacking |
Alt-Svc | HTTP/3 通告 |
7.3 状态码
5 类,每类前端必知典型:
- 1xx:100 Continue、101 Switching Protocols(WebSocket)、103 Early Hints
- 2xx:200、201 Created、204 No Content、206 Partial
- 3xx:301(永久)、302(临时)、304 Not Modified、307/308(保留 method)
- 4xx:400、401(未认证)、403(无权限)、404、405、409(冲突)、413(body 太大)、415、429(限流)
- 5xx:500、502 Bad Gateway(上游错)、503 Service Unavailable、504 Gateway Timeout
401 vs 403:401 = 没登录(应去登录),403 = 已登录但无权限。前端处理逻辑不同。
302 vs 307:302 历史上有些浏览器会把 POST 改成 GET 重定向,307/308 严格保留 method。
8. 性能优化要点
8.1 减少 RTT
- 用 HTTP/2 / 3 多路复用
- 开 keep-alive
preconnect提前握手- 0-RTT(HTTP/3 + TLS 1.3)
8.2 减少 body
- gzip / brotli 压缩(brotli > gzip 约 20%)
- 图片格式:WebP、AVIF
- Tree shaking、按需加载
8.3 缓存
- 静态资源 hash 文件名 +
Cache-Control: max-age=31536000, immutable - HTML 用协商缓存 + 短 max-age
详见模块 09。
8.4 早期提示
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
HTTP/1.1 200 OK
...
让浏览器在等 200 时就开始下载关键资源。Nginx 1.25.3+ 支持。
9. 故障排查
9.1 看用的什么协议
# curl
curl -v --http2 https://example.com 2>&1 | grep "HTTP/"
# Chrome DevTools → Network → Protocol 列
9.2 看请求耗时分解
Chrome DevTools → Network → 单击请求 → Timing:
- Queueing:浏览器排队
- Stalled:等连接
- DNS Lookup
- Initial connection(TCP)
- SSL(TLS 握手)
- Request sent
- Waiting (TTFB):服务端处理
- Content Download
TTFB 长 = 服务端慢。Stalled 长 = 浏览器并发限制(HTTP/1.1)或 keep-alive 失效。
9.3 502 / 504 区别
- 502 Bad Gateway:代理(Nginx)收到上游不合法响应(连接被重置、上游崩)
- 504 Gateway Timeout:代理等上游超时
9.4 426 Upgrade Required
服务端要求客户端升级协议。WebSocket 握手失败常见。
10. 安全考量
- 强制 HTTPS:HTTP 明文,中间人随便看。HSTS preload 让浏览器永远走 HTTPS
- 关 HTTP/1.0:老协议安全审计差
- 限制 method:禁用 TRACE、CONNECT
- 响应头加固:CSP、HSTS、X-Content-Type-Options、X-Frame-Options
- gzip bomb:服务端解压请求 body 时限大小,防 100KB 压缩 → 1GB 解压
11. 常见反模式
- HTTP/2 还在做域名分片:反而慢
- HTTP/2 拼接 sprite:HTTP/2 多个小请求不再贵,sprite 反而失去按需加载
- 大量小文件不开 HTTP/2:每个文件一次 TCP,慢得离谱
- HTTP/3 不通告 Alt-Svc:客户端不知道,永远走 HTTP/2
- HTTP/3 端口不放 UDP:防火墙拦截,客户端 fallback 回 HTTP/2
- Cache-Control 写 no-cache 当不缓存:no-cache = 协商缓存,要写 no-store
- 302 redirect 链 > 3 跳:每跳 RTT,性能差
Connection: close在生产:丢失 keep-alive,QPS 折半
12. 延伸阅读
- 《Web 性能权威指南》(High Performance Browser Networking)Ilya Grigorik — 在线免费
- RFC 7540 HTTP/2
- RFC 9114 HTTP/3
- HTTP/3 explained — Daniel Stenberg(curl 作者)
- Cloudflare Blog: HTTP/3
- HTTP Working Group — 最新规范动态