Skip to content

HTTP 报文结构与请求方法

基于 HTTP 标准(RFC 9110 语义)· 核于 2026-06

速查

  • 协议定位:HTTP 是应用层协议,跑在可靠的 TCP 之上(HTTP/3 例外,基于 QUIC/UDP),采用请求—响应模型,无状态(stateless,服务器默认不记得上一次请求),媒体无关(任意字节流都能传,靠 Content-Type 标注)。
  • 请求报文四段请求行(方法 + 请求目标 + 版本)→ 首部字段(headers)→ 空行(一个 CRLF)→ 消息体(body,可选)。
  • 响应报文四段状态行(版本 + 状态码 + 原因短语)→ 首部空行
  • 空行是分界线:首部与消息体之间必须有一个空行(CRLF),它标志「元数据结束、正文开始」,缺了就会解析错乱。
  • 请求目标四形式origin-form/path?query,最常见)、absolute-form(带完整 URL,走代理时用)、authority-formhost:port,仅 CONNECT)、asterisk-form*,仅 OPTIONS *)。
  • 方法三大属性安全 safe(只读不改服务器状态)、幂等 idempotent(请求 N 次 = 请求 1 次的效果)、可缓存 cacheable(响应可被缓存复用)。
  • 安全方法:GET、HEAD、OPTIONS、TRACE(其中 GET/HEAD 才常被缓存)。
  • 幂等方法:GET、HEAD、PUT、DELETE、OPTIONS、TRACE;POST 非幂等PATCH 不保证幂等
  • 可缓存方法:默认仅 GET、HEAD;POST/PATCH 仅在响应显式带新鲜度信息 + 匹配 Content-Location 时才可缓存。
  • GET vs POST:差别在语义/幂等/缓存/可见性,不是「GET 更快」;GET 读、可缓存、参数在 URL 上可见,POST 写、默认不缓存、数据在体里。
  • PUT vs POST:PUT 幂等地整体替换指定 URI 的资源,POST 非幂等地提交(常由服务器决定新资源 URI)。
  • PUT vs PATCH:PUT 整体替换,PATCH 局部更新(只发要改的字段)。
  • 2026 现状:九个标准方法(GET/HEAD/POST/PUT/DELETE/CONNECT/OPTIONS/TRACE/PATCH)均现行有效,无废弃;统一由 RFC 9110《HTTP Semantics》定义。

HTTP 协议是什么

HTTP(HyperText Transfer Protocol,超文本传输协议)是 Web 的基石,理解它的几个本质特征,后面的报文与方法才不会死记硬背:

  • 应用层协议:位于 OSI/TCP-IP 模型最上层,负责「我要拿什么、你给我什么」的业务语义,底层的连接、可靠传输交给传输层。
  • 基于可靠传输:HTTP/1.1 与 HTTP/2 跑在 TCP 上,由 TCP 保证字节有序、不丢不重;HTTP/3 改用 QUIC(基于 UDP,但自带可靠性与多路复用)。HTTP 本身不操心丢包重传。
  • 请求—响应模型:永远是客户端先发请求、服务器后回响应,一来一回。服务器不能主动「推」一条响应给闲着的客户端(服务端推送是另一套机制,不在本页范围)。
  • 无状态(stateless):这是 HTTP 最关键的设计。协议本身不在两次请求之间保留任何记忆——服务器处理完一个请求就「忘了」客户端是谁。登录态、购物车这类「状态」是靠 Cookie、Session、Token 等额外手段在无状态协议上叠出来的,不是 HTTP 自带的。
  • 文本/明文(HTTP/1.1):HTTP/1.1 的报文是人类可读的纯文本,可以直接用 telnetcurl -v 看到每个字节。这也是本页讲解报文结构的基础。HTTP/2、HTTP/3 改成了二进制分帧(不再是肉眼可读文本),但语义完全一致——方法、状态码、首部这些概念都没变,只是编码方式不同。
  • 媒体无关(media independent):HTTP 能传任意类型的数据——HTML、JSON、图片、视频、二进制文件都行,靠 Content-Type 首部声明「这是什么」,收发双方据此解析。

本页只讲 HTTP/1.1 文本报文

报文结构、字段顺序、空行分界这些规则,最直观的载体就是 HTTP/1.1 的文本格式,本页围绕它展开。HTTP/2/3 的二进制分帧、HPACK/QPACK 头压缩、多路复用等细节留到「HTTP 演进与性能」叶讲——但请记住:它们传的还是同一套 HTTP 语义

请求报文结构

一条 HTTP 请求报文由四个部分自上而下组成:请求行 → 首部字段 → 空行 → 消息体

http
POST /users HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 49

name=FirstName+LastName&email=bsmth%40example.com

逐段拆解:

请求行(request-line)

第一行,格式为 方法 SP 请求目标 SP HTTP版本SP 为空格),以 POST /users HTTP/1.1 为例:

  • 方法(method):本次操作的语义动词,如 GETPOSTPUTDELETE,详见下文「请求方法」。
  • 请求目标(request-target):要操作的资源标识,最常见是 /path?query 形式(详见「请求目标的四种形式」)。
  • HTTP 版本(version):如 HTTP/1.1,告诉服务器按哪个版本的规则解析。

首部字段(headers)

请求行之后是若干首部字段,每行一个,格式 字段名: 值字段名大小写不敏感Hosthost 等价)。首部承载请求的元数据,例如:

  • Host:目标主机(HTTP/1.1 必填,一个 IP 上靠它区分多个站点)。
  • Content-Type:消息体的媒体类型(如 application/json)。
  • Content-Length:消息体的字节长度。

空行(CRLF)

首部之后是一个空行(一个独立的 CRLF)。它是首部与消息体的分界:服务器读到空行,就知道首部到此为止,后面(如果有)全是消息体。这个空行不可省略

消息体(body)

空行之后是消息体,承载实际要发送给服务器的数据(表单内容、JSON 负载、上传文件等)。

哪些方法带消息体?

按 RFC 9110,消息体在语义上主要伴随 POSTPUTPATCH 出现;GETHEADDELETE 通常没有消息体(给 GET 塞 body 在规范上「无定义语义」,多数服务器会忽略,别这么用)。下文方法属性表给出了每个方法的「请求体」列。

响应报文结构

响应报文与请求对称,也是四段,只是第一行从「请求行」换成了「状态行」:状态行 → 首部 → 空行 → 体

http
HTTP/1.1 201 Created
Content-Type: application/json
Location: http://example.com/users/123

{
  "message": "New user created",
  "user": {
    "id": 123,
    "firstName": "Example",
    "lastName": "Person"
  }
}

状态行(status-line)

第一行,格式 HTTP版本 SP 状态码 SP 原因短语,以 HTTP/1.1 201 Created 为例:

  • HTTP 版本:如 HTTP/1.1
  • 状态码(status-code):三位数字,表达结果类别,如 200404500(细节见下一页)。
  • 原因短语(reason-phrase):状态码的人类可读描述,如 CreatedNot Found。它仅供人看,程序判断结果应只认状态码数字,不要解析短语文本(不同实现的短语可能不同,HTTP/2/3 更是直接不传短语)。

首部、空行、体

与请求报文同理:状态行后是响应首部(如 Content-TypeLocationSet-Cookie),然后一个空行,最后是响应体

有些响应没有体

并非所有响应都带体。204 No Content304 Not Modified按规范不允许有消息体;对 HEAD 请求的响应也不能有体(HEAD 的定义就是「要 GET 的首部,但不要体」)。这些情况下,空行之后就直接结束了。

请求目标的四种形式

请求行里的「请求目标」并非只有 /path 一种写法,RFC 9110 定义了四种形式,由方法和场景决定用哪种:

形式写法典型场景示例
origin-form绝对路径 + 查询串绝大多数普通请求GET /search?q=http HTTP/1.1
absolute-form完整 URL(含协议与主机)请求发往正向代理GET https://example.com/path HTTP/1.1
authority-form主机:端口CONNECT(建立隧道,如 HTTPS 代理)CONNECT example.com:443 HTTP/1.1
asterisk-form单个 *OPTIONS(询问服务器整体能力,而非某资源)OPTIONS * HTTP/1.1

日常 99% 是 origin-form

浏览器直连服务器时,请求行里的目标就是 /path?query 这种 origin-form——主机信息单独放在 Host 首部里,不重复写进请求行。absolute-form 主要在显式配置了正向代理时才出现;authority-formasterisk-form 各自绑定在 CONNECT 与 OPTIONS 上,平时几乎见不到。

请求方法

方法(也叫「HTTP 动词」)声明了客户端想对资源做什么。先逐个认识常用方法,再统一看它们的三大属性。

常用方法逐个看

  • GET获取资源的表示(representation)。只该「读」,不该改服务器状态;参数通过 URL 查询串携带,无消息体。最常见、可缓存。
  • POST:向指定资源提交数据,通常引起状态变化或副作用(如新建一条记录、提交表单、触发处理)。数据放在消息体里。非幂等。
  • PUT:用请求体的内容整体替换目标 URI 上的资源(「把这个 URI 的内容设成我给的这份」)。幂等——同样的 PUT 发一次和发十次,资源最终状态一致。
  • DELETE删除指定资源。幂等——删一次和「删一次再重复删(已不存在)」,最终都是「没了」这个状态。
  • PATCH:对资源做局部修改(只发要改动的部分,而非完整资源)。不保证幂等(取决于补丁格式与实现)。
  • HEAD:与 GET 完全相同,但只要首部、不要消息体。用于探测资源是否存在、查看 Content-Length/Last-Modified 等元信息而不下载正文。安全、幂等、可缓存。
  • OPTIONS:询问目标资源(或服务器,用 OPTIONS *支持哪些通信选项,响应常通过 Allow 首部列出可用方法。CORS 预检请求就是用它。安全、幂等。

TRACECONNECT 较少在业务代码中直接使用,了解即可:

  • TRACE:沿请求路径做回环测试(把收到的请求原样回显),用于诊断。出于安全考虑(可能泄露敏感首部),很多服务器默认禁用。
  • CONNECT:请求服务器建立一条隧道到目标(用 authority-form 给出 host:port),最典型用途是 HTTPS 流量经过正向代理时建立 TCP 隧道。

没有被废弃的方法

截至 2026-06,上述九个方法在 RFC 9110 中全部现行有效,无一废弃。日常 RESTful 接口里用得最多的是 GET / POST / PUT / DELETE / PATCH 这五个。

方法的三大属性

衡量一个方法「能不能放心重试、能不能缓存」,看三个属性:

  • 安全(safe):调用它不会改变服务器状态(纯只读)。安全方法可以被搜索引擎爬虫、预取机制随意调用而无副作用。
  • 幂等(idempotent):用相同参数调用 N 次,对服务器的最终影响与调用 1 次相同(注意:是「最终状态相同」,不要求每次响应内容字节一致,比如计数器读取值可能变)。幂等方法在网络出错时可以安全重试
  • 可缓存(cacheable):响应允许被缓存并在后续复用。能否真正缓存还取决于响应的 Cache-Control 等首部。

安全 ⊆ 幂等,但反过来不成立

所有安全方法必然幂等(不改状态当然多调几次也一样);但幂等不等于安全——DELETE 幂等却显然不安全(它改了服务器状态,删除了资源)。两个概念别混。

下表汇总每个方法的属性(依据 RFC 9110 / MDN):

方法安全幂等可缓存请求体
GET
HEAD
POST有条件¹可选
PUT
DELETE
PATCH否²有条件¹
OPTIONS可选
TRACE
CONNECT可选

¹ POST、PATCH 默认不可缓存;仅当响应显式包含新鲜度信息(如 Cache-Control/Expires带匹配的 Content-Location 时才可缓存——实践中极少这么做。 ² PATCH 不保证幂等:是否幂等取决于补丁内容(如「把字段设为 5」幂等,「把库存减 1」就不幂等)。

GET vs POST:别再说「GET 更快」

这是最常被误解的对比。两者的真正区别在语义与属性,而非性能:

维度GETPOST
语义读取资源(不改状态)提交数据(常改状态/有副作用)
安全是(只读)
幂等是(可安全重试)否(重试可能重复提交)
可缓存是(默认)默认否
数据位置URL 查询串(可见、会进历史/日志)消息体(不显示在 URL 上)
长度限制受 URL 长度实际限制(浏览器/服务器各有上限)体长度限制宽松得多

常见误区

  • 「POST 比 GET 安全」:POST 只是不把数据放在 URL 上,本身不加密。真正的传输安全靠 HTTPS,GET/POST 走 HTTPS 时都加密。
  • 「GET 更快」:两者性能差异微乎其微,选择应基于语义(读用 GET、写用 POST),不是速度。
  • 「GET 不能带数据」:GET 能带查询参数,只是放在 URL 上而非消息体,且总量受 URL 长度限制。
  • POST 非幂等的实际后果:表单重复提交、网络超时后盲目重试,都可能造成「下两次单」。需要可重试的写操作,考虑用幂等的 PUT,或引入幂等键(idempotency key)。

PUT vs POST vs PATCH

三个「写」方法的边界经常含糊,按下面的语义区分:

  • PUT vs POST —— 谁定 URI、幂不幂等

    • PUT 是「把我指定的这个 URI 的资源,整体设成我给的内容」。客户端知道目标 URI,且幂等PUT /users/123 发多次,123 这条记录最终都是同一份内容。
    • POST 是「把数据提交给这个集合/端点,具体生成什么、放在哪由服务器决定」。常用于新建(如 POST /users,服务器分配新 ID 并在 Location 首部返回新资源 URI)。非幂等,多发就多建。
  • PUT vs PATCH —— 整体替换 vs 局部更新

    • PUT整体替换:请求体须是资源的完整表示,未提供的字段会被视为「设为空/默认」。
    • PATCH局部更新:请求体只含要改动的部分,未提及的字段保持不变。改一个字段用 PATCH 更省、更清晰。
http
# PUT:整体替换 —— body 是完整资源
PUT /users/123 HTTP/1.1
Content-Type: application/json

{ "id": 123, "name": "Alice", "email": "alice@example.com", "age": 30 }
http
# PATCH:局部更新 —— body 只含要改的字段
PATCH /users/123 HTTP/1.1
Content-Type: application/json

{ "email": "new@example.com" }

RESTful 约定速记

新建用 POST(服务器定 URI)或 PUT(客户端定 URI 且需幂等);整体改用 PUT;局部改用 PATCH;删除用 DELETE;读取用 GET。需要在不稳定网络上安全重试的写操作,优先选幂等的 PUT/DELETE。

小结

HTTP 是无状态的应用层请求—响应协议,请求/响应报文都由「起始行 → 首部 → 空行 → 体」四段构成,而方法的安全 / 幂等 / 可缓存三属性,决定了一个接口能否被缓存、能否安全重试——这正是设计 RESTful API 时取舍的依据。报文走完了,下一步看服务器如何用三位数字回应结果:HTTP 状态码全谱