HTTP 报文结构

HTTP 报文(message)是协议的基本通信单位。无论是浏览器还是服务器,相互收发的都是一段格式固定的纯文本(HTTP/1.x)。这一章我们把报文拆开看清楚——以后再看 F12 Network 就不慌了。

1. 报文的两种角色

HTTP 报文分两种:

两者的结构高度相似,都是"起始行 + 头部 + 空行 + 可选消息体"四段式。唯一区别是起始行内容不同:请求的叫请求行,响应的叫状态行

2. 请求报文:四段式

一个完整的 HTTP 请求报文长这样:

GET /api/users?page=1 HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: application/json
Accept-Language: zh-CN,zh;q=0.9
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwp
Cookie: sessionid=abc123; theme=dark

(这里是一个空行,标记头部结束)

逐段拆解:

3. 响应报文:四段式

响应报文结构对称:

HTTP/1.1 200 OK
Server: nginx/1.25.0
Date: Tue, 05 Aug 2026 08:30:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 47
Connection: keep-alive
Cache-Control: max-age=60

{"page":1,"users":[{"id":1,"name":"Alice"}]}

逐段拆解:

4. 那个"空行"为什么重要?

HTTP 协议规定:头部和消息体之间必须有一个空行(CRLF)。这不是排版,是语法——服务器/客户端就是靠它判断"头结束了,下面都是 body"。

# 这个空行不是装饰,是协议强制的!
# 服务器读到 \r\n\r\n (空行) 就知道: 头部结束,后面都是消息体
GET / HTTP/1.1\r\n
Host: example.com\r\n
\r\n            <-- 这一行就是空行 (CRLF)
(body 开始)

这也是早期"请求头注入"攻击的根源:如果服务器把用户输入直接拼到响应头里,攻击者注入一个 \\r\\n\\r\\n,就能"提前结束头部 + 伪造 body"。

5. 行结束符:CRLF

HTTP/1.x 报文里每一行都以 CRLF(即 \\r\\n,回车 + 换行)结尾,而不是 Unix 单换行 \\n。实际开发你几乎不用关心——浏览器、curl、服务器都会帮你处理好——但自己用 socket 手写报文时千万别用 \\n 偷懒,部分严格的服务器会直接拒绝。

6. 用 curl 看原始报文

调试 HTTP 最快的办法是 curl -i(打印响应头)或 curl -v(同时打印请求和响应):

# 用 telnet/curl 手搓一个最朴素的请求
# 终端里能清楚看到原始报文长什么样
curl -i http://example.com/

# -i 让 curl 把响应头一起打印出来
# 输出:
# HTTP/1.1 200 OK
# Content-Type: text/html; charset=UTF-8
# Date: Tue, 05 Aug 2026 08:30:00 GMT
# Server: EOS (lax51/67A8)
# Content-Length: 1256
#
# <!doctype html> ...

7. 消息体长度:Content-Length vs 分块

服务器怎么知道 body 在哪里结束?两种方式:

# 大文件/流式响应用 Transfer-Encoding: chunked
# Content-Length 不写,改成 chunked
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

5\r\n
Hello\r\n
7\r\n
, world\r\n
0\r\n
\r\n

# 每块格式: 长度(十六进制) \r\n 数据 \r\n
# 最后用长度 0 的块标记结束

8. HTTP/2 之后:报文不再"肉眼可见"

HTTP/2 起,报文从纯文本变成二进制分帧:一个请求/响应被切成多个 frame(HEADERS frame、DATA frame),可以乱序发送、多路复用。但语义没变——还是"方法 + 路径 + 头 + body"那一套,只是 wire format 变了。所以学 HTTP/1.1 报文结构完全不会过时

小结

记住四段式:起始行 → 头部 → 空行 → 消息体。请求的起始行是"方法 URL 版本",响应的是"版本 状态码 短语"。空行是协议语法、不能省。Content-Length 或 chunked 决定 body 怎么收尾。

← 上一篇 HTTP 简介

下一篇 HTTP 请求方法

✈️💬