本节摘要:HTTP 是浏览器与服务器之间的请求响应协议,无状态、简单可靠。本节以版本演化为线索讲透其核心机制:报文结构、无状态与 Cookie、1.1 的长连接与管道化、2 的多路复用与首部压缩、3 转投 QUIC 的动因——每次演进都是对上一章传输层痛点的正面回应。
语法书翻开。HTTP 的本质极简:客户端发一个请求(方法加路径加首部),服务器回一个响应(状态码加首部加正文)。所有版本演化都在优化同一件事——让更多请求更快地跑在 TCP 这条管道上。
一次最原始的 HTTP/1.0 对话: 客户端请求: GET /index.html HTTP/1.0 ← 请求行:方法 路径 版本 Host: www.example.com ← 首部:键值对元信息 User-Agent: curl/8.0 ← 空行 = 首部结束标志 (无正文,GET 的参数在 URL 里) 服务器响应: HTTP/1.0 200 OK ← 状态行:版本 状态码 短语 Content-Type: text/html ← 正文类型与长度 Content-Length: 1256 <html>... 1256 字节正文 ... 状态码三段记忆:2xx 成功(200 正常、204 无正文、206 断点续传) 3xx 重定向(301 永久、302 临时、304 缓存仍有效——省正文的重定向) 4xx 客户端错(401 未认证、403 拒绝、404 不存在、429 太频繁) 5xx 服务器错(500 内部、502 网关收到坏响应、504 网关等超时) 无状态:服务器不记得你是谁,每个请求独立。 Cookie 补丁:首次响应 Set-Cookie: session=abc123, 之后每次请求自动带上——会话状态存进了客户端, 服务器只存一个映射。这个 1994 年的补丁沿用至今, 也是第 7 章会话劫持风险的源头。
HTTP/1.0 的痛点:每个请求开一条新 TCP 连接—— 每个对象都要三次握手加慢启动(上一章刚学过:慢启动从 1 MSS 起)。 一个 50 张图的页面 = 50 次慢启动从零爬坡。 HTTP/1.1(1997,服役最久): 长连接 keep-alive:一条连接顺序发多个请求,握手与慢启动摊薄 管道化 pipelining:可以连发多个请求不等应答—— 但响应必须按序返回,前一个慢后面全堵(队头阻塞), 实际几乎没人敢开 缓存与断点续传:强缓存协商缓存、Range 请求 HTTP/2(2015): 二进制分帧:报文拆成更小的帧,一个连接上交错传输 多路复用:真正并发,请求响应乱序匹配(靠流 ID)—— 理论上消灭了应用层队头阻塞 首部压缩 HPACK:重复的 Cookie 等首部增量编码, 体积砍掉八九成 但 TCP 层队头阻塞仍在:一个包丢了,TCP 按序交付, 所有流一起等重传(5.1 节 QUIC 动机的另一半) HTTP/3(2022): 弃 TCP 投 QUIC(跑在 UDP 上): 流级独立重传,一条流丢包不拖累其他流 握手与加密合并,建连 1 RTT 起步 连接迁移:换网络不断线

# 请求头里看连接复用 $ curl -v -o /dev/null https://www.example.com 2>&1 | grep -E "^(\*|<)" * Connected to www.example.com (93.184.216.34) port 443 < HTTP/2 200 ← 服务器与 curl 协商出了 HTTP/2 < content-type: text/html; charset=utf-8 # ALT-SVC:HTTP/3 的过渡桥梁 $ curl -sI https://www.cloudflare.com | grep -i alt-svc alt-svc: h3=":443"; ma=86400 ← 服务器广播:我也说 HTTP/3, 端口 443,可用一天 解读:浏览器第一次仍走 HTTP/2,记住 ALT-SVC 后, 后续尝试升级到 QUIC;失败自动回退——协议切换没有断崖, 靠的就是这个平滑通告机制(任何大迁移都值得抄的设计)。 # 浏览器开发者工具的 Network 面板,Protocol 列直接显示 # h2 / h3 / http/1.1,刷新一个大型站点即可看到混合使用。
背景:某图片站升级 HTTP/2 后,部分弱网用户报告更慢了。
分析:HTTP/2 把所有请求压进一条 TCP 连接。 弱网丢包率 3% 时,单一连接的队头阻塞比 1.1 时代"6 条并行连接各自重传"更糟—— 6 条连接时丢包只堵住六分之一流量, 1 条连接时丢包堵住全部。 处置:服务端针对高丢包 UA 降级到 1.1 加多域名分片; 并在低丢包环境继续享受 h2 的首部压缩收益。 启示:协议没有银弹,只有权衡——h2 的收益前提是 "低丢包、高带宽"的现代网络;这正是 h3 用 QUIC 在流级别拆开重传的动机,也是为什么 h3 在弱网下 提升最明显。选型永远要问"我的用户网络长什么样"。
⚠️ 常见坑:把 304 当错误。它是缓存协商的胜利——服务器说"你缓存的那份还有效,正文不发了",是最省流量的成功响应。排错时看网络面板先滤掉 304 再数真正的失败。
REST 是协议吗? 不是。REST 是基于 HTTP 的架构风格约定:资源用 URL 表达、动作用方法表达、状态用状态码表达。HTTP 是法定语法,REST 是社区习惯——分清这层关系,你就明白为什么两个"REST 接口"可能风格迥异。
PUT 与 POST 有本质区别吗? 语义约定上 PUT 幂等(同一请求重复执行结果不变)、POST 不幂等。这个约定直接影响重试安全性:网关重试 PUT 放心,重试 POST 可能重复下单——工程上为 POST 补幂等键,正是 6.3 节方法论的应用。
语法书读完,下一节把 DNS 与 HTTP 身上的设计智慧提炼成可复用的方法论。