HTTP/1.1, HTTP/2 and HTTP/3 Deep Dive
Methods, headers, status codes, keep-alive, chunked encoding, multiplexing, QUIC — the protocol backend developers debug every day.
The request/response grammar
HTTP is a plain-text, stateless, request-response protocol. A request is: method, path, version, headers, blank line, optional body. A response mirrors it: version, status, headers, blank line, body.
GET /api/orders?limit=20 HTTP/1.1 ← 请求行
Host: api.example.com ← HTTP/1.1 唯一必填头
Authorization: Bearer eyJhbGciOi...
Accept: application/json
← 空行(没有 body 也必须有)
HTTP/1.1 200 OK ← 状态行
Content-Type: application/json
Content-Length: 128 ← 或 Transfer-Encoding: chunked
← 空行
{"orders": []} ← 正文
| Category | Codes | Meaning for the backend dev |
|---|---|---|
| 1xx 信息化 | 100 Continue | POST 大 body 前先问「要不要我发」 |
| 2xx 成功 | 200 / 201 / 204 | OK / 新建成功 / 成功但无正文 |
| 3xx 重定向 | 301 / 302 / 304 | 永久跳转 / 临时跳转 / 缓存仍然有效 |
| 4xx 客户端错 | 400 / 401 / 403 / 409 / 429 | 参数错 / 未认证 / 无权限 / 冲突 / 限流 |
| 5xx 服务端错 | 500 / 502 / 503 / 504 | 内部错误 / 网关错误 / 服务不可用 / 网关超时 |
HTTP/1.1: keep-alive, pipelining and its ceiling
HTTP/1.1 keeps the TCP connection open (keep-alive) so subsequent requests reuse it — huge win over 1.0's reconnect-every-time. But requests on one connection are serialized; browsers work around it by opening 6 parallel connections per host.
# 大响应分块流式发送(不知道总长时用)
HTTP/1.1 200 OK
Transfer-Encoding: chunked
7\r\n ← 这个块的字节数(hex)
hello, \r\n
9\r\n
backend! \r\n
0\r\n ← 长度 0 = 结束
\r\n
- Head-of-line blocking at the HTTP level: request 2 waits for request 1 on the same connection
- Domain sharding (static.example.com) was a hack to get more parallel connections — obsolete with HTTP/2
- Content-Length when you know it, chunked when you don't (streaming, SSE, generated responses)
HTTP/2 and HTTP/3: multiplexing and QUIC
| Feature | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP + TLS | TCP + TLS | QUIC(UDP) |
| 二进制分帧 | 无 | 有 | 有 |
| 多路复用 | 无(每连接串行) | 一个连接并发 N 个流 | 同 HTTP/2 |
| 队头阻塞 | HTTP 层 + TCP 层 | 解决 HTTP 层,仍留 TCP 层 | 两个都解决 |
| Server Push | 无 | 有(实践中已基本弃用) | 弃用 |
| 连接建立 | 1-3 RTT | 1-2 RTT | 0-1 RTT(首包更快) |
| 连接迁移 | 不可能 | 不可能 | 可以(IP 变了连接不断) |
Enable HTTP/2 by simply using https with a modern Nginx (listen 443 ssl http2;). HTTP/3 needs UDP/443 open in the firewall + QUIC-capable server (Nginx 1.25+, Caddy, Cloudflare free tier).
请求与响应的文法
HTTP 是纯文本、无状态、一问一答的协议。请求 = 方法 + 路径 + 版本 + 头 + 空行 + 可选正文;响应结构对称:版本 + 状态 + 头 + 空行 + 正文。
GET /api/orders?limit=20 HTTP/1.1 ← 请求行
Host: api.example.com ← HTTP/1.1 唯一必填头
Authorization: Bearer eyJhbGciOi...
Accept: application/json
← 空行(没有 body 也必须有)
HTTP/1.1 200 OK ← 状态行
Content-Type: application/json
Content-Length: 128 ← 或 Transfer-Encoding: chunked
← 空行
{"orders": []} ← 正文
| 类别 | 状态码 | 对后端的含义 |
|---|---|---|
| 1xx 信息化 | 100 Continue | POST 大 body 前先问「要不要我发」 |
| 2xx 成功 | 200 / 201 / 204 | OK / 新建成功 / 成功但无正文 |
| 3xx 重定向 | 301 / 302 / 304 | 永久跳转 / 临时跳转 / 缓存仍然有效 |
| 4xx 客户端错 | 400 / 401 / 403 / 409 / 429 | 参数错 / 未认证 / 无权限 / 冲突 / 限流 |
| 5xx 服务端错 | 500 / 502 / 503 / 504 | 内部错误 / 网关错误 / 服务不可用 / 网关超时 |
HTTP/1.1:长连接与它的天花板
HTTP/1.1 复用 TCP 连接(keep-alive),后续请求不再重连——比 1.0 的「每次都连」是巨大提升。但同一连接上的请求只能串行;浏览器只好对每个域名开 6 个并行连接绕开。
# 大响应分块流式发送(不知道总长时用)
HTTP/1.1 200 OK
Transfer-Encoding: chunked
7\r\n ← 这个块的字节数(hex)
hello, \r\n
9\r\n
backend! \r\n
0\r\n ← 长度 0 = 结束
\r\n
- HTTP 层的队头阻塞:同一连接上请求 2 必须等请求 1 完成
- 域名分片(static.example.com)是为了多开并行连接的奇技淫巧——HTTP/2 时代已过时
- 知道长度用 Content-Length,不知道用 chunked(流式、SSE、动态生成)
HTTP/2 与 HTTP/3:多路复用与 QUIC
| 特性 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP + TLS | TCP + TLS | QUIC(UDP) |
| 二进制分帧 | 无 | 有 | 有 |
| 多路复用 | 无(每连接串行) | 一个连接并发 N 个流 | 同 HTTP/2 |
| 队头阻塞 | HTTP 层 + TCP 层 | 解决 HTTP 层,仍留 TCP 层 | 两个都解决 |
| Server Push | 无 | 有(实践中已基本弃用) | 弃用 |
| 连接建立 | 1-3 RTT | 1-2 RTT | 0-1 RTT(首包更快) |
| 连接迁移 | 不可能 | 不可能 | 可以(IP 变了连接不断) |
启用 HTTP/2 只需 https + 现代 Nginx(listen 443 ssl http2;)。HTTP/3 需要防火墙开放 UDP/443 + 支持 QUIC 的服务端(Nginx 1.25+、Caddy、Cloudflare 免费层)。