1. 先把HTTP协议的基本盘弄清楚
1.1 HTTP不是"传输协议",是"应用层语义协议"
很多人一听到HTTP,第一反应是"TCP/IP那一层的某种传输方式"。这个理解从一开始就跑偏了。HTTP(HyperText Transfer Protocol,超文本传输协议)虽然跑在TCP之上,但它本质上是应用层协议,解决的不是"怎么把数据从一个设备搬到另一个设备",而是"客户端和服务器之间用什么样的格式去表达一次请求和一次响应"。
我经常拿点餐打比方:TCP负责把菜单从你手里安稳地送到后厨,再把菜品端回来,这是"跑腿";而HTTP是菜单上怎么写菜名、写几个人吃、要不要辣、后厨做完后在托盘上贴什么标签。没有HTTP,TCP只能把一堆字节搬来搬去,双方根本不知道这些字节是什么意思。
正因为如此,HTTP协议有一个非常关键的特性:无状态。每一次HTTP请求都是独立的,服务器默认不会记得你上一次请求干了什么。所以后面才有了Cookie、Session、Token这一整套"会话记忆"方案。你在浏览器里登录了一次,下次再访问,其实是浏览器自动带了Authorization头或Cookie,让服务器通过这个额外信息认出你。
再往深一层,HTTP请求归根结底就是一段有固定格式的文本。文本本身不复杂,真正复杂的是协议承载的语义:缓存、重定向、认证、内容协商、跨域、代理转发……这些功能全是靠"请求行 + 头字段 + 消息体"这三段结构堆出来的。把这套基本结构吃透,后面排查任何状态码、任何网关报错,你都会有一个清晰的坐标系。
1.2 请求报文和响应报文的真实结构
我见过不少工作了三五年的后端,写接口没问题,但让他用curl -v看一眼完整报文,他反而愣住了。其实HTTP报文结构非常简单,就是"起始行 + Header + 空行 + Body"。
请求报文长这样:
http复制POST /api/login HTTP/1.1
Host: example.com
Content-Type: application/json
User-Agent: curl/8.0.1
Accept: */*
{"username":"admin","password":"123456"}
第一行叫请求行,包含方法(POST)、路径(/api/login)、HTTP版本(HTTP/1.1)。接下来是若干Header键值对,每一行用"key: value"表示。空行用来分隔Header和Body。然后是请求体,这里是一段JSON。
响应报文结构类似:
http复制HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 40
Cache-Control: no-store
{"code":0,"token":"abc123def456"}
第一行叫状态行,包含HTTP版本、状态码(200)、状态描述(OK)。后续是响应Header和响应体。
有几个细节值得特别说明:
- 报文里换行符必须是
\r\n(回车+换行),不是单纯的\n。手写HTTP客户端时最容易在这里翻车,后面嵌入式小节我会专门讲。 Content-Length表示Body的字节数。如果这个值跟实际Body长度不一致,接收方要么读到一半阻塞,要么把下一次响应错位读取,这是很多"请求卡死"问题的根源。- 解析响应时,Header和Body之间一定有一个空行,按行读取时要特别处理。
- HTTP/1.1默认开启Keep-Alive,一个TCP连接可以连续发送多个请求,不做
Connection: close的话连接不会立刻断开。
用curl -v看一下真实报文是最直观的:
bash复制curl -v https://api.github.com/users/octocat
输出里>开头的是请求头,<开头的是响应头,*是TLS、DNS、TCP连接等过程信息。这个命令是我日常排查的第一板斧,看到完整报文之后,很多"玄学问题"立刻就不玄了。
1.3 方法与Header:后端必知字段
HTTP方法说白了就是"你希望服务器对这个资源干什么"。最常用的四个:
- GET:获取资源,不改变服务器状态,幂等。
- POST:创建资源或触发一个动作,非幂等。
- PUT:整体替换资源,幂等。
- DELETE:删除资源,幂等。
- PATCH:部分更新资源,也常被使用。
很多团队把POST当成万能方法,增删改查全部POST,表面上省事了,实际上丢失了语义。比如一个下载接口,你用了POST,CDN的缓存策略、网关的幂等重试逻辑都没法按标准语义工作。该用GET就用GET,该用PUT就用PUT,这不是教条,而是让整条链路上的中间件都能理解和配合你。
Header字段里,有几个是日常必然要遇到的:
Content-Type:告诉对方Body是什么格式。application/json、application/x-www-form-urlencoded、multipart/form-data各自对应不同的编码方式。很多400错误就是因为前端声明了application/json,实际发的却是form格式,或者反过来。Accept:请求方声明自己能解析的响应格式。如果服务器只支持application/json,而请求头写Accept: text/html,部分服务端会返回406 Not Acceptable。Authorization:携带认证凭证,常见格式是Bearer <token>或者Basic base64(username:password)。Cookie:浏览器自动携带的会话凭证。Cache-Control:控制缓存行为,比如no-store、max-age=60。Referer:记录请求来源页面,CTF题目里经常要求伪造它来绕过服务端来源校验。X-Forwarded-For:代理服务器追加的客户端原始IP,也经常被用来做IP校验。
Header还有一个容易被忽略的点:名称大小写不敏感,content-type和`Content-Typ
