本文全面介绍了 HTTP 协议相关知识,包括 HTTP 请求报文、响应报文、持久连接、缓存、Cookie 以及 HTTP 版本升级等核心内容。
HTTP 协议全称为 HyperText Transfer Protocol,即超文本传输协议。
超文本:指文字、图片、音频、视频、文件等的混合体,比如最常见的 HTML。
传输:指数据从一方转移到另一方,二者之间可能相距数千里。
协议:指通信双方所做的一些约定,比如怎么开始通信、信息的格式与顺序、怎么结束通信等。
那么,HTTP 协议究竟是干什么的呢?答案就是 用于客户端与服务器端之间的通信。我们日常上网过程中最常见的就是 HTTP 协议了,浏览器就是最典型的 HTTP 客户端。

举个具体的例子,当我们使用浏览器访问淘宝时,浏览器会向淘宝服务器发送一个遵循 HTTP 协议的 请求报文,告诉服务器自己想要获取淘宝首页的信息。服务器收到请求后,会返回一个同样遵循 HTTP 协议的 响应报文,其中包含了淘宝首页的内容。浏览器收到响应后,便会解析内容并将其展示在界面上。

1. HTTP 请求
客户端向服务器端发送的信息称为 请求报文,其标准结构如下图所示:

(1)请求行
请求行用于说明客户端想要做什么,包含方法、资源路径和 HTTP 版本三部分,中间用空格分割。
- 方法,指定要对请求资源执行的操作(如查询、修改)。常见方法有:GET、POST、PUT、DELETE、HEAD 等。
在前后端分离开发中,常遵循 RESTful 设计风格,使用 POST、DELETE、PUT、GET 分别对应数据的增、删、改、查。
- 资源路径,指定所请求资源在服务器中的位置。例如
/index.html,表示访问服务器根目录下名为 index 的 HTML 文件。
- HTTP 版本,指定所使用的 HTTP 协议版本。目前使用最广泛的是 HTTP/1.1。
下面是一个请求行的例子:

面试中常问的一个问题是:GET 和 POST 的区别是什么? 我们可以从几个层面来理解:
- 首先,参数位置不同。 通常 GET 请求的参数会附加在 URL 中(即查询字符串),而 POST 请求的参数则放在请求体中。
因为参数直接暴露在 URL 中,所以 GET 请求相对不那么安全。但这不代表 POST 就绝对安全,如果不加密,抓包同样能看到明文。此外,浏览器对 URL 长度有限制(如 IE 最大 2KB),因此 GET 请求传输的数据长度受限,而 POST 理论上无此限制。
- 其次,用途与特性不同。 通常 GET 请求用于获取数据,POST 请求用于新增数据。这涉及到 幂等性 的概念。幂等性意味着对同一资源的多次相同请求,其效果与单次请求一致,不会产生额外副作用。GET 请求是幂等的(只读),而 POST 请求不是(创建新资源)。基于此,GET 请求可以被缓存、会保留在浏览器历史记录中,且浏览器回退操作无害;POST 请求则相反。
- 最后,本质无区别。 HTTP 的底层是 TCP,GET 和 POST 在连接通信层面并无不同。理论上,我们可以给 GET 加请求体,给 POST 带上 URL 参数,甚至用 GET 新增数据、用 POST 查询数据,虽然不符合惯例,但技术上可行。

(2)请求头
请求头用于向服务器传递一些附加的重要信息,例如客户端能接受的语言、期望的响应类型等。
请求头由字段名和字段值构成,用冒号分隔。常见的请求头如下:
| 请求头 |
含义 |
| Host |
接收请求的域名 |
| User-Agent |
客户端软件(如浏览器)的名称和版本等信息 |
| Connection |
设置响应后 TCP 连接是否保持 |
| Cache-Control |
控制缓存的相关信息 |
| Referer |
记录当前请求的来源页面 URI |
| Accept |
客户端可接受的数据类型(MIME 类型) |
| Accept-Encoding |
客户端可支持的编码格式(如 gzip) |
| Accept-Language |
客户端可支持的语言 |
| If-Modified-Since |
用于缓存验证(携带本地缓存的最后修改时间) |
| If-None-Match |
用于缓存验证(携带本地缓存的 ETag 值) |
| Range |
用于断点续传,指定请求字节范围 |
| Cookie |
携带客户端的状态信息,用于身份识别 |
(3)请求空行
请求空行(即一个空行)用于标识请求头部分已经结束。
(4)请求体
请求体用于传送客户端要发送给服务器的数据,例如 POST 方法的表单参数。GET 方法通常没有请求体。
请求行和请求头是格式化的文本,而请求体则可以包含任意二进制数据,如文本、图片、视频等。
2. HTTP 响应
服务器向客户端返回的信息称为 响应报文,其标准结构如下图所示:

(1)响应行
响应行用于说明服务器对请求的处理结果,包含 HTTP 版本、状态码和消息短语三部分,中间用空格分割。
- HTTP 版本,例如
HTTP/1.1。
- 状态码,三位数字,描述请求的处理结果,如
200 表示成功。
- 消息短语,文本描述,如
OK 表示成功。
响应行示例:

另一个常见的面试问题是:HTTP 有哪些常见状态码? 我们先看状态码的分类:

下面是一些需要牢记的常见状态码:
200 OK:请求成功,最常见的状态码。
204 No Content:请求成功,但响应报文不含响应体(无内容返回)。
301 Moved Permanently:永久重定向。资源已永久移至新 URL,响应头 Location 字段会给出新地址,浏览器会自动跳转。
- 场景:网站更换域名,如
http://www.360buy.com 重定向到 http://www.jd.com。
302 Found:临时重定向。资源临时使用新 URL,浏览器也会自动跳转。
- 场景:用户未登录时访问个人中心,临时重定向到登录页;或 HTTP 跳转到 HTTPS。
304 Not Modified:资源未修改,客户端可使用本地缓存。
400 Bad Request:请求报文存在语法错误(客户端错误)。
401 Unauthorized:请求需要用户认证(未认证)。
403 Forbidden:服务器理解请求,但拒绝执行(未授权)。
404 Not Found:请求的资源在服务器上不存在。
500 Internal Server Error:服务器内部错误,无法完成请求。
502 Bad Gateway:服务器作为网关或代理时,从上游服务器收到无效响应。
503 Service Unavailable:服务器暂时过载或正在维护,无法处理请求。
(2)响应头
响应头用于向客户端传递一些附加的重要信息,例如响应的内容类型、长度等。
响应头同样由字段名和字段值构成。常见的响应头如下:
| 响应头 |
含义 |
| Date |
服务器生成响应的日期和时间 |
| Server |
HTTP 服务器软件信息 |
| Location |
配合重定向使用,提供新的 URI |
| Connection |
设置响应后 TCP 连接是否保持 |
| Cache-Control |
控制缓存的相关信息 |
| Content-Type |
响应正文的类型(MIME类型) |
| Content-length |
响应正文的长度 |
| Content-Encoding |
响应正文的编码(如 gzip) |
| Content-Language |
响应正文的语言 |
| Last-Modified |
资源的最后修改时间 |
| Expires |
资源过期时间(绝对时间) |
| Etag |
资源的唯一标识(哈希值),用于缓存验证 |
| Accept-Ranges |
服务器是否支持范围请求(用于断点续传) |
| Set-Cookie |
设置 Cookie,用于在客户端保存状态 |
(3)响应空行
响应空行用于标识响应头部分已经结束。
(4)响应体
响应体包含了服务器要返回给客户端的实际数据,如 HTML 文档、图片等。
浏览器收到响应报文后,会解析响应体并将其渲染展示出来。
3. HTTP 持久连接
客户端发送一系列请求给服务器,如果每个请求/响应对都使用一个独立的 TCP 连接,则称为非持续连接(短连接);如果多个请求/响应对复用同一个 TCP 连接发送,则称为持续连接(长连接)。
例如,打开一个包含一个 HTML 文件和两张图片的网页。如果使用三个独立的 TCP 连接分别获取这三个资源,就是非持续连接;如果只建立一个 TCP 连接来顺序获取所有资源,就是持续连接。

非持续连接的缺点很明显:
- 增加延迟:每个请求都要经历 TCP 三次握手建立连接,增加了整体响应时间。
- 服务器负担重:每个连接都需要服务器分配和维护资源(缓冲区、变量等),当同时服务大量客户端时负担沉重。
因此,HTTP/1.1 及以后的版本默认采用持续连接。这通过报文中的 Connection 头字段来控制:
Connection: keep-alive:启用持久连接(HTTP/1.1 默认,可省略)。
Connection: close:本次通信后关闭连接。
需要注意的是,持久连接并非永久连接。服务器通常会设置一个超时时间(如 keepalive_timeout),如果在一段时间内连接上没有新的请求,服务器就会主动关闭它。
4. HTTP 缓存
对于一些不常变化的资源(如图片、CSS、JS文件),浏览器可以将其缓存到本地。后续请求时,如果缓存有效,就直接使用本地数据,无需再向服务器发起请求,这能极大提升页面加载速度。
“缓存”的思想在计算机领域无处不在:CPU 有 L1、L2 缓存来加速数据读取;后端系统用 Redis 作为数据库缓存;操作系统用 TLB 快表缓存页表项。其核心目的都是为了提升数据访问速度。

HTTP 缓存也不例外,主要目的是加快网页响应。其实现依赖于请求和响应报文中的特定字段,分为 强缓存 和 协商缓存 两种机制。
(1)强缓存
强缓存 是指,只要浏览器判断本地缓存未失效,就直接使用缓存,不会向服务器发送任何请求。
实现强缓存主要通过 Cache-Control 和 Expires 这两个响应头字段。
Expires:指定一个绝对的资源过期时间(HTTP/1.0)。
Cache-Control:指定一个相对的资源过期时长(HTTP/1.1),优先级高于 Expires。

由于服务器和客户端可能存在时间偏差,使用 Expires 可能不够准确。因此,更推荐使用 Cache-Control 来实现强缓存。
Cache-Control 常见的指令有:
max-age=秒:缓存的有效期,例如 cache-control: max-age=31536000 表示缓存一年。
no-store:禁止使用任何缓存(强缓存和协商缓存都不使用)。
no-cache:不使用强缓存,但可以使用协商缓存。每次使用缓存前,必须去服务器验证缓存是否有效。相当于 max-age=0, must-revalidate。
must-revalidate:允许缓存。缓存过期前可直接使用;过期后,必须去服务器验证有效性,验证失败(如无法连接服务器)会返回504错误,而不是使用过期缓存。
强缓存的基本流程如下:

- 浏览器首次请求资源,服务器在响应头中设置
Cache-Control: max-age=xxx。
- 浏览器再次请求同一资源时,先检查缓存是否过期(根据
max-age 计算)。
- 如果未过期(且非
no-cache/no-store),直接使用本地缓存。
- 如果已过期,则进入协商缓存流程。
(2)协商缓存
协商缓存 发生在:强缓存失效(过期),或者响应头明确设置了 Cache-Control: no-cache。此时,浏览器会携带验证信息向服务器发起请求,由服务器判断缓存是否还能使用。
- 如果资源未更新(缓存有效),服务器返回
304 Not Modified,告诉浏览器继续使用缓存,并可能更新缓存有效期。
- 如果资源已更新,服务器返回
200 OK 和新资源。

协商缓存可以通过两对请求/响应头来实现:
第一种(基于修改时间 - HTTP/1.0):
- 服务器响应头:
Last-Modified(资源的最后修改时间)。
- 浏览器请求头:
If-Modified-Since(值为上次收到的 Last-Modified)。
- 流程:服务器比较
If-Modified-Since 的时间与资源的实际最后修改时间。
- 如果时间相同(未修改),返回
304。
- 如果时间不同(已修改),返回
200 和新资源。
- 缺点:1) 依赖时间,可能不精确(秒级);2) 文件内容未变但修改时间变了,会导致无效的重新下载。

第二种(基于内容标识 - HTTP/1.1,推荐):
- 服务器响应头:
ETag(资源的唯一标识符,通常是一个哈希值)。
- 浏览器请求头:
If-None-Match(值为上次收到的 ETag)。
- 流程:服务器比较
If-None-Match 的值与资源当前的哈希值。
- 如果匹配(未修改),返回
304。
- 如果不匹配(已修改),返回
200 和新资源。
- 优点:精确判断内容变化。缺点:计算
ETag 会消耗一些服务器资源。

注意事项:
- 当响应头中同时存在
ETag 和 Last-Modified 时,ETag 的优先级更高。
Ctrl + F5 强制刷新:浏览器会忽略所有缓存,直接向服务器请求最新资源。
F5 刷新或点击刷新按钮:浏览器会在请求头中带上 Cache-Control: max-age=0,即跳过强缓存,直接走协商缓存。
5. Cookie
HTTP 协议本身是无状态的,不会记录之前的请求和响应信息。这简化了服务器设计,但也带来了问题:例如电商网站无法记住用户的登录状态。
为了解决“保持状态”的需求,Cookie 应运而生。你可以把 Cookie 理解成服务器发给客户端的“会员卡”,里面记录了用户的身份信息。下次访问时,客户端出示这张“卡”,服务器就知道你是谁了。

Cookie 的工作流程:
- 首次请求:客户端(如浏览器)请求服务器,请求中没有 Cookie。
- 服务器响应:服务器在响应头中通过
Set-Cookie 字段,给客户端“颁发”一个包含唯一标识(如用户ID)的 Cookie。
- 后续请求:浏览器会自动在请求头的
Cookie 字段中带上这个标识。
- 服务器识别:服务器根据 Cookie 中的标识,查询到对应的用户信息(如姓名、购物车),从而实现状态保持。

Cookie 的主要属性:
max-age / expires:设置 Cookie 的过期时间(相对/绝对)。max-age=0 或过期时间已到,Cookie 会被删除。
secure:标记为 Secure 的 Cookie 只会在 HTTPS 协议加密的请求中被发送。
HttpOnly:设置了 HttpOnly 的 Cookie 不能被 JavaScript 访问,有助于防止跨站脚本攻击(XSS)窃取 Cookie。
domain:指定 Cookie 对于哪个域是有效的。默认不能跨域,但可以设置给父域,实现子域共享。
Cookie 的缺点在于,如果存储信息过多,会导致每次请求的头部变大,影响传输效率。浏览器通常对单个 Cookie 的大小限制在 4KB 左右。
6. HTTP 版本演进
随着互联网的发展,HTTP 协议也在不断迭代升级,旨在提升性能、安全性和效率。下面我们梳理一下从 HTTP/1.1 到 HTTP/3 的核心改进。
(1)HTTP/1.1 相比 HTTP/1.0 的改进
① 持久连接
如前所述,HTTP/1.1 默认使用持久连接,一个 TCP 连接可以发送多个请求,减少了频繁建立和断开连接的开销。
② 管道机制
管道机制允许客户端在同一个连接上,不必等待上一个请求的响应返回,就可以发出下一个请求。这旨在减少整体的响应时间。

然而,服务器必须按照接收请求的顺序依次返回响应。如果前一个请求处理很慢,就会阻塞后续所有请求的响应,这被称为 队头阻塞。因此,管道机制在实际中并未被广泛支持,更多是通过建立多个并行 TCP 连接来提升性能。

③ 增强的缓存控制
引入了 Cache-Control、ETag 等更强大的缓存控制头部,替代或补充了 HTTP/1.0 的 Expires 和 Last-Modified。
④ 断点续传
通过 Range 和 Accept-Ranges 头部支持范围请求,便于大文件的分块下载和断点续传。
(2)HTTP/2 相比 HTTP/1.1 的改进
① 头部压缩
HTTP/1.1 的头部信息冗余且庞大。HTTP/2 引入了 HPACK 压缩算法,客户端和服务器维护一张静态和动态的“头部表”,将高频头部字段用索引号表示,大大减少了头部传输的数据量。后续请求中,相同的头部字段只需发送差异部分。

② 二进制分帧
HTTP/2 不再使用纯文本报文,而是将报文拆分为更小的二进制帧(如 HEADERS 帧、DATA 帧)。二进制协议解析效率更高,也更紧凑。
③ 多路复用
这是 HTTP/2 的核心改进。HTTP/2 在同一个 TCP 连接上引入了 流 的概念,每个请求/响应被分配一个唯一的流ID。多个流的帧可以交错发送和接收,真正实现了并行传输,彻底解决了 HTTP 层面的队头阻塞问题。

④ 服务器推送
服务器可以主动向客户端推送资源,而无需客户端明确请求。例如,服务器在响应 HTML 页面时,可以主动将页面所需的 CSS 和 JS 文件推送给客户端,减少额外的请求延迟。

(3)HTTP/3 相比 HTTP/2 的改进
尽管 HTTP/2 解决了应用层的队头阻塞,但其底层依然依赖 TCP。TCP 是面向字节流的可靠协议,一旦发生丢包,整个连接必须等待重传,这就是 TCP 层的队头阻塞。HTTP/3 的诞生就是为了解决这个问题。
① 基于 QUIC,无队头阻塞
HTTP/3 的最大变革是底层传输协议从 TCP 换成了 UDP,并在 UDP 之上实现了名为 QUIC 的新协议。QUIC 在应用层实现了可靠的传输。由于 UDP 是无连接的,不同流之间完全独立,一个流的丢包不会影响其他流,彻底解决了队头阻塞。

② 改进的重传机制
TCP 的重传超时(RTO)计算存在歧义(无法区分 ACK 是对原始包还是重传包的确认)。QUIC 使用单调递增的 Packet Number,即使重传相同数据,包编号也不同,从而能精确计算 RTT(往返时间),提升拥塞控制效率。

③ 连接迁移
一条 TCP 连接由四元组(源IP、源端口、目的IP、目的端口)标识。网络切换(如WIFI切4G)导致IP变化时,TCP 连接必须重建。QUIC 使用一个64位的连接ID来唯一标识连接。只要连接ID不变,即使网络环境变化导致IP和端口改变,连接依然可以复用,实现了无缝迁移。
(4)总结
HTTP 协议的演进始终围绕着性能、安全和用户体验。从 HTTP/1.1 的持久连接,到 HTTP/2 的多路复用和头部压缩,再到 HTTP/3 的基于 QUIC 彻底解决队头阻塞,每一步都旨在让 Web 更快、更稳、更高效。

以上就是对 HTTP 协议核心知识的系统梳理。理解这些概念,无论是进行前端、后端开发,还是进行网络问题排查,都至关重要。如果你想深入了解更多 计算机网络 知识,欢迎到 云栈社区 交流探讨。