HTTP 方法:安全与幂等
更新时间:2026-09-17。网络协议域·应用层。方法语义在 HTTP/1.1、2、3 中保持一致(见 HTTP/1.1)。幂等性是分布式重试、超时去重的基石。
理解 HTTP 方法有两个正交的形容词:安全(safe)与幂等(idempotent)。它们不是协议强制的行为,而是方法应当遵守的语义契约——服务端可以违约(GET 也能改数据),但那会破坏缓存、爬虫、重试等一堆假设。
一、两个概念
- 安全(Safe):请求语义是只读的,不应引起服务器状态变化。安全方法天然可被缓存、被爬虫放心访问、遇失败可随意重试。
- 幂等(Idempotent):同一个请求执行一次和执行多次,对服务器资源状态的最终效果相同。幂等不等于"不修改",而是"重复修改结果可预期、不叠加"。
数学类比:幂等像把值"设为 5"(设多少次都是 5),非幂等像"加 1"(执行 N 次结果不同)。
二、方法对照表
| 方法 | 安全 | 幂等 | 语义 | 有 body |
|---|---|---|---|---|
| GET | ✅ | ✅ | 读取资源 | 通常无 |
| HEAD | ✅ | ✅ | 只要响应头(无 body) | 无 |
| OPTIONS | ✅ | ✅ | 查询能力/CORS 预检 | 无 |
| PUT | ❌ | ✅ | 用请求体整体替换/创建(客户端指定 URI) | 有 |
| DELETE | ❌ | ✅ | 删除资源 | 可有 |
| POST | ❌ | ❌ | 创建子资源/任意处理(语义最宽泛) | 有 |
| PATCH | ❌ | 通常❌ | 部分修改 | 有 |
DELETE 也是幂等的:第一次删除资源,第二次再删时资源已不存在——从"资源最终不存在"的状态看结果相同(可返回 204 或 404,但不应叠加副作用)。PATCH 是否幂等取决于怎么写:
{set status=closed}幂等,{counter +=1}不幂等。
三、PUT 与 POST 的区别
经典混淆点。二者都可能"创建资源",区别在谁决定 URI、是否幂等:
PUT /users/123(body 为完整资源):客户端指定资源标识,整体替换;同样的 PUT 发 N 次,/users/123 的内容还是那份 → 幂等。POST /users(body 为新资源):由服务端分配标识并通常返回 201 + Location;发 N 次会创建 N 条 → 不幂等。
四、为什么幂等在工程上关键
1. 超时与自动重试
客户端发了请求、响应却丢了/超时——到底成功没有?能不能安全重发?
- 对幂等方法(GET/PUT/DELETE),可直接重试,最坏只是重复一次同样的操作。
- 对 POST,盲目重试可能重复扣款、重复下单、重复发货。
负载均衡、SDK、gRPC、浏览器预取是否敢自动重试,依据的就是安全/幂等性。
2. 幂等键(Idempotency-Key)
要让"非幂等的创建/支付"也能安全重试,常见做法是客户端生成唯一幂等键:
POST /payments
Idempotency-Key: 7e9f-...-a3
Content-Type: application/json
{"order":123,"amount":100}服务端记录该 key 的首次处理结果;相同 key 重试时不重复执行业务,直接返回首次结果(响应缓存)。这把"非幂等操作"在客户端视角变成幂等,是支付/订单系统的标准设计(Stripe 等 API 的模式)。
3. QUIC/HTTP 的 0-RTT 早期数据
HTTP/3 的 0-RTT 允许在恢复连接的首包就带请求数据,存在被中间人重放的风险,因此规范建议 0-RTT 只承载安全且幂等的请求(如 GET),非幂等的 POST/PUT 应等 1-RTT 后发——见 HTTP/3 与 QUIC。
五、状态码与方法配合
- GET 成功 200;POST 创建常 201 Created(带 Location);无 body 的成功 204 No Content(DELETE/PUT 常见)。
- 方法不允许返回 405 Method Not Allowed(应带 Allow 头列出支持的方法)。
- 缓存:GET/HEAD 的响应可被缓存(受 Cache-Control 等约束),POST 默认不缓存,安全方法是 HTTP 缓存体系成立的前提。
六、实践建议
- 严格按语义实现:别让 GET 修改状态(会被预取/爬虫/CDN 意外触发)。
- 删除/更新接口优先设计成幂等(DELETE 重复调用、PUT 全量替换)。
- 涉及钱/库存的 POST 用幂等键 + 唯一约束 + 状态机,保证"至多生效一次"。
- 客户端重试策略:幂等方法可自动重试;非幂等需配合幂等键或人工确认,并区分"超时未确认"与"明确失败"。
相关阅读
传输与连接见 HTTP/1.1/HTTP/2/HTTP/3,keep-alive 复用,TCP 层的可靠与重传见 TCP 重传。
总结
安全=只读无副作用(GET/HEAD/OPTIONS),幂等=多次执行最终资源状态与一次相同(GET/PUT/DELETE,POST 不幂等,PATCH 视实现);幂等性决定了超时能否安全重试,非幂等的支付/创建要靠 Idempotency-Key 在服务端去重,QUIC 0-RTT 也只该携带安全幂等请求——它是方法语义契约,不是强制行为,违约会破坏缓存、重试和中间件假设。