一、HTTP 概述
本节先厘清 HTTP 的定位:它传输什么、处在网络模型的哪一层、用在哪些场景,以及它的通信模型。这些是后面理解报文、方法和缓存的基础。
1. 超文本传输协议
HTTP(HyperText Transfer Protocol,超文本传输协议)能传输的不仅仅是字符串,还有多媒体数据(二进制)。这也是它被称为"超文本"而不是"纯文本"的原因。
2. HTTP 是应用层协议
HTTP 在传输层主要依赖 TCP 协议,从 HTTP/3 开始切换到了 UDP。之所以切换,核心原因在于:HTTP/3 底层使用 QUIC 实现可靠传输,不再完全依赖 TCP 的重传机制;而 TCP 的拥塞控制在内核中实现,迭代慢,对性能影响明显。
3. HTTP 的应用场景
- Web 开发中:网页前端和服务器后端之间的通信。
- 移动端 App:和服务器后端之间的通信。
- 分布式系统:服务器之间的调用。
4. 一问一答模型
HTTP 是典型的"一问一答"模型:客户端发送一个请求,服务器返回一个响应。
要点回顾:HTTP 是应用层协议,采用一问一答模型;HTTP/3 基于 QUIC 走 UDP,可靠传输由 QUIC 承担。
二、HTTP 请求和响应
本节看 HTTP 报文长什么样。HTTP 是纯文本协议,报文结构可以直接读懂,掌握请求报文和响应报文的四段式结构,是后续抓包分析的前提。

1. Request 请求报文
http
GET https://www.bilibili.com/ HTTP/1.1
Host: www.bilibili.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: text/html,application/xhtml+xml
Cookie: buvid3=xxxx
(空行,说明 header 部分结束)
(请求正文,GET 通常省略)
- 请求行:方法 + URL + 协议版本。
- 请求报头:若干行键值对,每行一个,键和值之间用"冒号 + 空格"分隔,含义由 HTTP 协议规定。
- 空行:说明 header 部分结束。
- 请求正文:可能省略。
2. Response 响应报文
http
HTTP/1.1 302 Found
Location: https://www.bilibili.com/index.html
Content-Type: text/html; charset=utf-8
Content-Length: 0
(空行,说明 header 部分结束)
(响应正文,可能省略)
- 状态行:协议版本 + 状态码 + 状态描述。
- 响应报头:同样是若干行键值对,含义由 HTTP 协议规定。
- 空行:说明 header 部分结束。
- 响应正文:可能省略。
要点回顾:请求报文是"请求行 + 报头 + 空行 + 正文",响应报文是"状态行 + 报头 + 空行 + 正文",空行是 header 与 body 的分界。
三、URL 网址
本节拆解 URL 的各个组成部分,并说明 query string 为什么需要编码。理解 URL 结构,才能明白后面 Host 头、路径和查询参数各自的作用。

URL(Uniform Resource Locator,统一资源定位符)的作用是表示一个网络上的位置。
以 https://mdec.nelreports.net/api/report?cat=mdocs 为例,逐段拆解:
- 协议 :
https。URL 里还可以带登录信息(user:password@),但为了方便使用和防止写错,现在基本都是走登录页面认证,这种写法基本不用了。 - 端口号:没写端口号时,浏览器会根据协议类型给一个"知名端口号"作为默认值,HTTP 不写就是 80,HTTPS 不写就是 443。
- 服务器地址 :
mdec.nelreports.net。这里写的是域名,直接写 IP 地址也是可以的。 - 带层次的文件路径 :
/api/report。通过 IP 确定机器,通过端口确定程序;一个服务器程序可能会有很多资源,为了方便管理,就按照一定目录结构来组织(可能是真实的目录,也可能是通过代码逻辑构造的"虚拟目录结构")。 - 查询字符串 :
?cat=mdocs。query string 也是键值对结构的数据,HTTP 的键值对是协议定义好的,这里的键值对是程序员自己定义的。 - 片段标识符 :
#fragment,一般用来区分一个页面中的不同部分。注意:fragment 不会发送给服务器,它只在浏览器本地生效。
URL encode
query string 是程序员自定义的,如果包含了特殊符号呢?这时候就需要 encode 进行转码,URL 中很多特殊符号都是有特定含义的。
特殊符号需要转义,中文和空格等也通常会被浏览器编码:把要转义的内容每个字节拿出来,用十六进制表示,每个字节前面加上 %。
text
https://example.com/search?q=C%2B%20%26%20%E4%B8%AD%E6%96%87%20100%25
解码后:https://example.com/search?q=C+%26+中文+100%
要点回顾:URL 由协议、端口、主机、路径、查询字符串和片段标识符组成;fragment 不发给服务器;特殊字符需要 URL 编码。
四、认识方法
本节介绍 HTTP 方法及其语义差异,重点澄清 GET 与 POST 的几个常见误区。方法选择直接影响接口语义、幂等性和缓存行为。

几种常见方法的对比如下:
| 方法 | 语义 | 是否有 body | 幂等 |
|---|---|---|---|
| GET | 获取资源 | 通常没有 | 是 |
| POST | 提交数据 | 通常有 | 否(官方建议) |
| PUT | 更新资源,和 POST 类似 | 通常有 | 是 |
| DELETE | 删除资源 | 通常没有 | 是 |
body 里面放的是程序员自定义的数据。
1. GET 请求

对于 GET 请求来说,通常情况下是没有 body 的。GET 是从服务器拿一个数据,到底是哪一个数据?靠两部分信息定位:
- URL 中的路径;
- URL 中的 query string。
2. POST 请求

对于 POST 请求来说,通常情况下都是有正文的。常见情况:登录和上传文件。
3. GET 和 POST 的区别
两者没有本质区别(能用 GET,也能换成 POST;能用 POST,也能用 GET),但使用习惯上有一定区别:
- 语义不同:GET 的语义是获取数据,POST 的语义是提交数据。
- 幂等性不同:GET 一般实现成"幂等",POST 没有"幂等"(官方建议)。但实际中 POST 常被人为设置为幂等,防止重复点击导致操作被执行多次。幂等的含义是:多次请求结果保持一致、不会产生额外副作用。
- 传输数据量 :常说"GET 单次传输的数据量比较小,POST 比较大",这个说法不准确。真实原因是历史上浏览器和服务器对 URL 长度有上限,而现在 URL 也可以很长了,限制主要来自实现而非协议。
- 能否传二进制 :常说"GET 只能传文本,POST 能传文本和二进制",这个说法也不准确。二进制数据可以通过 urlencode / Base64 转换为文本,再通过 GET 传输;只是 URL 中不能直接放二进制数据,而且 Base64 会让体积膨胀约 33%。
注意:GET 与 POST 的差异主要来自语义约定和实现限制,而不是协议层面的硬性规定。把"GET 不能传大数据/二进制"当成协议规则,是常见的误区。
4. PUT 和 DELETE
POST 和 PUT 类似,只是 PUT 具有幂等性,一般用于更新,通常把数据也放到 body 里面。DELETE 和 GET 类似,通常没有 body。
要点回顾:方法的核心差异在语义和幂等性;GET/POST 在数据量和二进制上的常见说法并不准确,限制主要来自实现。
五、Host
本节围绕 Host 头展开,并延伸到 Content-Length、Content-Type 和浏览器缓存。这些头部决定了请求被路由到哪个站点、body 如何解析,以及资源是否走缓存。
1. Host 表示服务器主机的地址和端口

Host 会把请求目标的主机和端口单独列出;URL 中的域名还需要经过一系列解析才能得到 IP 和端口。
Host 头独立于 URL 存在,意义在于:
- 虚拟主机:一台服务器、一个 IP 上可以部署多个网站,靠 Host 区分访问的是哪个站点。
- CDN / 缓存分流:代理和 CDN 节点根据 Host 决定回源到哪个源站、命中哪份缓存。
2. Content-Length 和 Content-Type
- Content-Length:表示 body 中的数据长度,单位是字节。
- Content-Type:表示 body 的数据格式。
这两个字段和 body 一起出现,有 body 通常就需要这两个字段,请求报头和响应报头中都可能出现。
3. Content-Type 常见取值
下表列出常见 Content-Type 及其在浏览器中的处理方式:
| Content-Type | 含义 | 浏览器处理方式 |
|---|---|---|
| text/html | HTML 文档 | 按 HTML 规则解析,渲染成网页骨架 |
| text/css | CSS 样式 | 按 CSS 规则解析,渲染成网页样式 |
| application/javascript | JavaScript 脚本 | 执行 JS 中的逻辑 |
| application/json | JSON 数据 | 浏览器一般不自主处理,由页面中的 JS 代码负责解析 |
| image/jpeg | JPEG 图片 | 直接渲染显示 |
| image/png | PNG 图片 | 直接渲染显示 |
| text/plain | 纯文本 | 按纯文本展示 |
| application/x-www-form-urlencoded | 表单编码,类似 query string 的格式 | 由后端框架解析成参数 |
| multipart/form-data | 多部分表单,常用于文件上传 | 由后端框架按分段解析,二进制原样传输 |

CSS/JS 开发阶段的换行空格是正常的,线上版本是被专门的工具进行了压缩,可以节省体积和网络传输带宽。

4. 强制刷新与浏览器缓存

强制刷新可以从网络上重新获取,不读缓存。HTML(网页结构)、CSS(网页样式)、JavaScript(网页行为)、图片等都是变动频率比较低的数据,浏览器通常会把上述数据保存到本机硬盘上作为"缓存",下次直接从硬盘读取。
协商缓存里最常见的是 304 Not Modified :浏览器带着 If-Modified-Since 或 If-None-Match 去问服务器"资源变了没",没变就返回 304,浏览器继续用本地缓存,省掉正文传输。
要点回顾:Host 决定请求落到哪个站点;Content-Length 和 Content-Type 描述 body;304 是协商缓存命中的典型状态码。
六、User-Agent(简称 UA)
本节说明 UA 的组成和用途。UA 是服务端识别客户端环境、做兼容和分流的重要依据。

http
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Mozilla/5.0:历史遗留的兼容标识,几乎所有浏览器都这么写。Windows NT 10.0:操作系统的版本。Win64:64 位操作系统。- 后面是浏览器内核和版本信息。
UA 主要描述浏览器和操作系统的版本信息,常用来区分手机和 PC。
要点回顾:UA 描述客户端环境,常用于兼容判断和端类型分流。
七、Referer 和 Location
本节介绍两个与跳转相关的头部:Referer 记录来源页面,Location 指示重定向目标。理解它们有助于排查来源统计和重定向问题。
1. Referer

表示这个页面是从哪个页面跳转过来的。不是所有请求都有 Referer,以下情况通常没有:
- 直接在地址栏输入 URL;
- 直接访问收藏夹;
- 从 HTTPS 页面跳到 HTTP 页面(浏览器默认不发送);
- 页面设置了
Referrer-Policy: no-referrer; - 请求本身不是由页面跳转触发的(如部分直接发起的请求)。
2. Location
表示服务器告诉浏览器要去哪个网页,常用于重定向,一般和 3xx 状态码一起出现。
要点回顾:Referer 不一定存在,不能假设它总是可用;Location 与 3xx 状态码配合完成重定向。
八、Cookie
本节讲 Cookie 的作用、下发与回传方向、存储方式和典型用途,并厘清 Cookie 与 Session 的关系。这是理解登录态的基础。

1. 作用
Cookie 是浏览器侧的本地存储机制。安装的应用程序一般能访问硬盘,浏览器一般不能访问,这时候 Cookie 允许网页按照"键值对"的格式组织一个字符串,浏览器会把这个字符串保存到本地硬盘中。
2. 从哪里来、到哪里去
- 下发方向 :服务器通过响应头
Set-Cookie把要存在浏览器本地的 cookie 值传递过去。 - 回传方向 :本地存储以后,后续访问同一个域名的网站,浏览器会把 cookie 的内容通过请求头
Cookie传输给服务器。
3. 怎么存
按照字符串(键值对)方式来存储,根据域名维度划分,每个域名下有自己的 cookie,不同域名之间互相隔离。
4. 用途
用途有很多,但最常见、最核心的就是保存用户的登录状态。

5. Cookie 与 Session
服务端维护了一个 HashMap,key = sessionId,value = session 对象。Cookie 和 Session 不是强相关:登录也可以使用其他技术搭配 Session,其他场景下 Cookie 也可以完成其他功能。
要点回顾:Set-Cookie 下发、Cookie 回传;Cookie 按域名隔离;Cookie 与 Session 并非强绑定。
九、常见的状态码
本节汇总常见状态码,并重点区分容易混淆的 301/302 缓存差异和 502/504 的故障含义。状态码是排查线上问题最直接的线索。

常见状态码及其说明如下:
| 状态码 | 含义 | 说明 |
|---|---|---|
| 200 | OK | 成功 |
| 301 | Moved Permanently | 永久重定向,可被缓存 |
| 302 | Found | 临时重定向,默认不缓存 |
| 304 | Not Modified | 协商缓存命中,继续用本地缓存 |
| 403 | Forbidden | 拒绝访问 |
| 404 | Not Found | 访问的资源在服务器上不存在 |
| 405 | Method Not Allowed | 服务器不支持这个方法 |
| 500 | Internal Server Error | 服务器内部错误 |
| 502 | Bad Gateway | 网关/代理从上游收到无效响应 |
| 504 | Gateway Timeout | 网关/代理等待上游响应超时 |
4xx 都属于客户端错误,5xx 都表示服务器错误。502 和 504 的区别:502 是上游返回了坏响应,504 是上游没有在超时时间内响应。
301 和 302 的缓存差异:301 会被浏览器长期缓存,下次直接跳新地址;302 默认不缓存,每次仍会请求原地址。
注意:502 与 504 都常出现在网关/代理层,但含义不同------前者是上游响应无效,后者是上游超时未响应,排查方向也不同。
要点回顾:4xx 是客户端问题,5xx 是服务端问题;301 可缓存、302 默认不缓存;502 看上游响应,504 看上游超时。
十、构造 HTTP 请求
本节介绍构造 HTTP 请求的几种方式,并给出 Java 11+ HttpClient 的示例。理解如何手工构造请求,有助于验证协议细节和排查问题。
- 通过 form 表单构造 HTTP 请求:纯 HTML 的方案。
- 通过 AJAX 构造 HTTP 请求:通过 JavaScript 构造的 HTTP 请求。
- 通过 Java Socket 构造 HTTP 请求。
下面是 Java 11+ 的 HttpClient 示例:
java
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
public class MyHttpClient {
public static void main(String[] args) throws IOException, InterruptedException {
// 创建一个 http 实例,用工厂方法
HttpClient httpClient = HttpClient.newHttpClient();
// 创建一个 HttpRequest,表示一个 http 请求,链式结构
HttpRequest request = HttpRequest.newBuilder()
// 目标地址
.uri(URI.create("https://www.bilibili.com"))
.GET()
// 可以放 Cookie、Content-Length 之类的
.header("User-Agent", "xxxxxx")
.build();
// 发送给服务器 + 接收响应
// BodyHandlers 负责把 body 里面的内容转换为字符串
// send 会产生阻塞,直到服务器响应
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.uri());
System.out.println(response.body());
// 大文件不要用 ofString,直接落盘,避免整个文件读进内存
HttpResponse<Path> fileResponse = httpClient.send(request, HttpResponse.BodyHandlers.ofFile(Path.of("download.html")));
System.out.println(fileResponse.body());
}
}
也有不写代码的方案,使用专门的构造 HTTP 请求的工具:
- Postman
- Apifox
要点回顾 :构造请求可以用表单、AJAX、Socket 或 HttpClient;大文件响应应使用 ofFile 落盘,避免整体读入内存。
十一、Fiddler 的用法
本节介绍抓包工具的本质和使用方法。抓包是验证协议细节、定位前后端问题最直接的手段。
学习协议报文时,少不了抓包工具,这里使用的是 Fiddler。
1. 抓包工具的本质
通过软件读取网卡的内容,从而看到请求和响应的数据明细,本质上是一种"代理"。代理也分为"正向代理"(代替客户端干活)和"反向代理"(代替服务器干活)。
2. 用抓包定位 bug
如果要找一个功能的 bug,先抓包,通过网络传输的数据初步判定是前端出 bug 还是后端:
- 请求本身就不对(前端 bug);
- 请求正确,响应错误(后端 bug);
- 请求正确,响应正确(前端 bug)。
例如:网页中不能显示预期数据,就看请求与响应的详细情况。
3. 第一次使用需要设置

把相关选项全勾选上,弹出"是否要信任根证书"也要选 YES,根证书是 HTTPS 抓包的前提。

4. 配置完成以后要重启才会生效

Raw 会展示出 HTTP 原始的数据格式(如果想自己构造一个 HTTP 请求,就照着上述格式拼装一个字符串写到 TCP socket 里面),看到乱码说明响应内容被压缩了。
要点回顾:抓包工具本质是代理;通过请求与响应是否正确,可以快速划分前后端问题;HTTPS 抓包依赖信任根证书。