技术知识文章集合TECHNICAL ARCHIVE · 807 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTNET

听说『99% 的人都理解错了 HTTP 中 GET 与 POST 的区别』?

所属分类
NET Protocol
文件格式
Markdown
原始路径
NET Protocol/22-听说『99% 的人都理解错了 HTTP 中 GET 与 POST 的区别』??
本文目录11 个章节
  1. 写在前面
  2. 一、先说结论
  3. 二、这段说法是怎么来的?
  4. 三、POST 不一定等待 100 Continue
  5. 四、XHR 不等于 POST
  6. 五、HTTP 规范中真正的 GET 与 POST 区别
  7. 六、为什么不能用 TCP 包数量区分 GET 和 POST?
  8. 七、一个更准确的测试结论
  9. 八、原文观点总结
  10. 九、总结
  11. 参考资料

听说『99% 的人都理解错了 HTTP 中 GET 与 POST 的区别』?

Category(分类): NET Protocol Status: 未知

写在前面

今天看到一篇标题十分吓人的文章:

99%的人都理解错了 HTTP 中 GET 与 POST 的区别

这篇文章前半部分讨论了大家经常提到的 GET 和 POST 区别,重点在后面。原文提出了一个传播很广的说法:

GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。

并进一步解释:

对于 GET 请求,浏览器会把 HTTP Header 和 Data 一并发送出去;而对于 POST,浏览器先发送 Header,服务器响应 100 Continue,浏览器再发送 Data,服务器响应 200 OK。

这个说法把某些浏览器或 XHR 实现中的抓包现象,误写成了 GET 和 POST 的协议区别。本文保留这段历史讨论,但按照现代 HTTP 规范重新梳理它的适用范围。

一、先说结论

GET 和 POST 没有固定的 TCP 数据包数量。

下面这些说法都不能作为 HTTP 规范结论:

GET 一定是一个 TCP 包
POST 一定是两个 TCP 包
POST 一定先收到 100 Continue 才发送请求体

实际传输多少个 TCP 段,可能受到以下因素影响:

  • 请求行、首部和请求体的实际大小;
  • TCP MSS、路径 MTU 和分段方式;
  • 客户端 Socket 写入和缓冲策略;
  • Nagle 算法、TCP_NODELAY 和延迟 ACK;
  • TLS 记录切分和加密层缓冲;
  • HTTP/1.1、HTTP/2 或 HTTP/3 的实现;
  • 代理、负载均衡器和中间网络设备;
  • 丢包、重传和拥塞控制。

而且 HTTP/3 使用 QUIC over UDP,根本不存在“GET 和 POST 各产生几个 TCP 包”的问题。

二、这段说法是怎么来的?

为了验证上述说法,原文引用了一篇文章:

XMLHttpRequest (XHR) Uses Multiple Packets for HTTP POST?

这篇英文文章讨论的是某个浏览器版本中 XMLHttpRequest(XHR)发送 POST 时的具体网络表现,而不是 HTTP 规范规定的 GET 和 POST 必须使用不同数量的数据包。

原文还记录了当时的浏览器测试结果:

  • Chrome 55:观察到 two packets;
  • Safari 10:观察到 two packets;
  • Firefox 49:没有观察到 two packets。

这些结果可以作为当时浏览器实现的历史记录,但不能推广成所有浏览器、所有请求库、所有操作系统和所有网络环境的行为。浏览器版本、请求大小、是否启用 TLS、连接是否复用,以及抓包位置变化后,结果都可能不同。

三、POST 不一定等待 100 Continue

原文批评了以下说法,这个批评的方向是正确的:

POST 先发送 Header,服务器响应 100 Continue,浏览器再发送 Data。

1. 100 Continue 需要 Expect 请求头配合

现代 HTTP 规范定义了:当客户端发送请求头时,如果请求包含较大的请求内容,可以带上:

Expect: 100-continue

它表达的意思是:客户端希望服务器先根据请求头判断是否愿意接收请求体,然后再发送请求体。

服务器收到后可以:

  • 返回 100 Continue,提示客户端继续发送请求体;
  • 直接返回最终响应,例如 401 Unauthorized、403 Forbidden 或 413 Content Too Large;
  • 在请求体已经到达或没有请求体时,省略 100 Continue。

客户端收到 100 Continue 后才继续发送请求体,是一种由 Expect 触发的协商流程,不是 POST 方法的固定流程。

2. 普通 POST 不必等待 100 Continue

普通浏览器表单提交、Fetch 或 XHR POST 通常不会自动带上 Expect: 100-continue 并等待服务器响应。客户端可以把请求头和请求体连续交给操作系统发送,底层是否分成一个或多个 TCP 段由实现决定。

即使客户端使用了 Expect: 100-continue,100 Continue 也是 HTTP 层的临时响应,不是 TCP 握手,也不是一个专门的 TCP 数据包。客户端还应设置等待策略,不能因为一直没有收到 100 就无限等待。

因此,原文中“普通 POST 一定先收到服务器 100 Continue 才发送 body”的说法是错误的。

四、XHR 不等于 POST

原文指出:英文标题讨论的是 XMLHttpRequest,而中文文章却把 XMLHttpRequest 直接写成了 POST。这一点是正确的。

XHR 是浏览器提供的 JavaScript API,用于发起 HTTP 请求。它可以发起:

  • GET;
  • POST;
  • PUT;
  • PATCH;
  • DELETE;
  • HEAD 等请求。

Fetch API 也是类似的请求接口。XHR、Fetch、Axios 是发起请求的工具或 API,GET、POST 是 HTTP 方法,两者不是同一个概念。

因此,准确的说法应当是:

某个浏览器版本在通过 XHR 发送某类 POST 请求时,观察到请求数据被分成不同的网络写入或 TCP 段。

不能把它简化为“POST 天生发送两个 TCP 包”。

五、HTTP 规范中真正的 GET 与 POST 区别

当我们讨论 GET 和 POST 的区别时,应该首先讨论 HTTP 方法语义,而不是某个浏览器版本的实现细节。

对比项GETPOST
主要语义请求传输目标资源的当前表示请求目标资源按照自身语义处理提交的数据
安全方法是否
幂等性是默认不是
默认缓存性通常可缓存通常不可缓存,满足明确条件时才可能缓存
常见参数位置URI 查询参数请求体,也可以有查询参数
请求内容语义没有通用定义,服务器可能拒绝或忽略请求内容通常是资源需要处理的表示
常见用途查询、读取、获取资源表单提交、创建、上传、触发业务操作

1. GET 的安全性和幂等性

GET 是安全方法,表示客户端的请求意图是读取资源,而不是要求服务器修改资源。安全方法仍可能产生日志、统计等附带效果,“安全”不代表服务器完全没有任何内部变化。

GET 也是幂等的。重复发送相同 GET,按照规范的预期效果应当与发送一次相同。响应状态码、响应时间和日志数量仍可能不同,但请求的目标资源状态不应因为 GET 而被改变。

2. POST 默认不是幂等的

POST 请求目标资源处理请求体中的数据,常见于:

  • 提交表单;
  • 创建资源;
  • 上传文件;
  • 发起订单;
  • 执行某个业务动作。

重复提交 POST 可能创建两条记录、生成两个订单或执行两次操作,因此规范不保证 POST 幂等。如果业务确实需要重试,应通过幂等键、请求 ID 或业务去重机制实现,而不是假设 POST 自动幂等。

3. GET 和 POST 都可以有请求内容,但语义不同

HTTP 消息的请求内容是否存在,与 TCP 分成几个数据包是两回事。

GET 请求可以在语法层面携带内容,但现代 HTTP 规范指出,GET 请求内容没有一般性的定义,不能改变 GET 的目标或语义,服务器也可能拒绝。因此,客户端除非与目标服务器有明确约定,否则不应在 GET 中发送请求体。

POST 的请求内容则是其常见语义的一部分,服务器会根据资源定义处理它。请求体可以使用 Content-Length 或 Transfer-Encoding 等消息 framing 方式传输。

4. 缓存不是“GET 永远缓存、POST 永远不缓存”

GET 响应通常可以被缓存,但仍受 Cache-Control、Expires、Vary 等首部影响。

POST 响应通常不被通用缓存直接复用,但在提供明确的新鲜度信息、Content-Location 等条件下,规范允许特定缓存场景。实际缓存行为还取决于浏览器、CDN 和代理配置。

5. GET 参数在 URL,POST 参数在 body?

这是常见使用习惯,不是绝对规则:

  • GET 常把查询条件放在 URI 的 query 部分;
  • POST 常把提交数据放在请求体;
  • POST 也可以带 query 参数;
  • GET 请求体没有通用语义,不建议用它传业务数据。

6. GET 比 POST 更安全吗?

不能这样比较。GET 和 POST 都可能传输敏感数据,都应使用 HTTPS。

GET 查询参数可能出现在:

  • 浏览器历史记录;
  • Web 服务器访问日志;
  • 代理日志;
  • 监控系统;
  • Referer(具体取决于 Referrer-Policy);
  • 缓存键。

POST 请求体通常不显示在地址栏,但并不天然加密,也可能被服务器、代理、日志中间件记录。安全性主要取决于 TLS、日志策略、认证授权、CSRF 防护和应用实现,而不是仅由 GET/POST 决定。

六、为什么不能用 TCP 包数量区分 GET 和 POST?

1. HTTP 方法属于应用层语义

GET 和 POST 定义的是服务器如何理解请求,以及请求具有什么安全性、幂等性和缓存语义。TCP 只负责传输字节流,不理解 GET、POST、URL 或 HTTP 首部。

即使是 HTTP/1.1,请求也可能出现以下情况:

GET:请求头较小,可能被一次写入,也可能被拆成多个 TCP 段
POST:请求头和小请求体可能连续写入,也可能被拆成多个 TCP 段
POST:大请求体通常需要很多 TCP 段

一个 TCP 段也不等于一个 HTTP 请求,一个 HTTP 请求也不等于一个 TCP 段。TCP 的分段、重组、确认和重传都不会保留 HTTP 方法的边界。

2. HTTP/2 和 HTTP/3 进一步说明了这个问题

HTTP/2 通常运行在 TCP 之上。HTTP 请求会被编码成 HEADERS、DATA 等帧,并在多个 Stream 中复用。抓包时看到的是 HTTP/2 帧、TLS 记录和 TCP 段的组合,不能按 HTTP/1.1 的经验数包。

HTTP/3 运行在 QUIC 之上,QUIC 使用 UDP 而不是 TCP。此时继续讨论“GET 一个 TCP 包、POST 两个 TCP 包”就完全不适用了。

3. 抓包结果受观察位置影响

在浏览器开发者工具、操作系统网卡、代理服务器、TLS 终止点或服务端网卡处抓包,看到的内容可能不同。还需要区分:

  • TCP 三次握手;
  • TCP 数据段;
  • TCP ACK;
  • TLS 握手和 TLS Application Data;
  • HTTP/2 帧;
  • 重传数据段;
  • QUIC UDP 数据报。

所以抓包可以帮助分析某一次实现的行为,但不能仅凭一次抓包总结 HTTP 方法的规范差异。

七、一个更准确的测试结论

如果复现旧文章中的浏览器测试,应当这样描述:

在特定浏览器版本、特定请求大小、特定协议版本和特定网络环境下,通过 XHR 或其他 API 发出的 POST 请求,可能被浏览器分成多次 Socket 写入,并在抓包中表现为多个 TCP 数据段。其他浏览器或其他配置可能采用不同的发送方式。

还应说明:

  • “多个 Socket 写入”不等于“多个 TCP 数据包”;
  • “多个 TCP 数据包”不等于“POST 的协议规定”;
  • 是否有 100 Continue 取决于 Expect 和服务器处理;
  • TCP 可能合并或拆分这些写入;
  • 开启 HTTPS 后还会经过 TLS 记录层封装;
  • HTTP/2 和 HTTP/3 的传输模型又不同。

八、原文观点总结

原文认为“99% 的人理解错了 GET 和 POST”属于标题党,这个评价可以保留,但更准确的总结应当是:

  1. 不能把某一浏览器实现的抓包行为当成 GET/POST 的协议规定;
  2. XHR 是请求 API,POST 是 HTTP 方法,两者不是同一层次的概念;
  3. 100 Continue 与 Expect: 100-continue 有关,不是所有 POST 的固定步骤;
  4. GET 与 POST 的核心差异应该从 HTTP 方法语义、请求内容、是否安全、幂等性和缓存性来解释;
  5. TCP 数据包数量属于传输实现细节,不能作为 GET/POST 的通用区别;
  6. 在现代 HTTP/2 和 HTTP/3 环境中,用 TCP 包数量区分 GET 和 POST 更加不成立。

九、总结

GET 和 POST 的区别,首先是 HTTP 规范中的方法语义,其次才是浏览器和请求库的具体实现。

  • GET 通常用于获取资源,是安全且幂等的;
  • POST 通常用于提交数据或触发资源处理,默认不保证幂等;
  • GET 请求内容没有通用语义,POST 通常使用请求体携带要处理的内容;
  • GET 和 POST 都可能被拆成多个 TCP 段;
  • POST 不一定触发 100 Continue;
  • Expect: 100-continue 是可选的请求协商机制;
  • XHR、Fetch 和 Axios 是请求 API,不等于 POST;
  • GET 和 POST 都需要 HTTPS 才能保护传输中的敏感信息。

参考资料

发布于 2017-01-26 19:38(本文根据现代 HTTP 规范补充整理)

807 DOCUMENTS · 12 COLLECTIONS
ARCHIVE SEARCH807 篇文章