302 是临时重定向,本意是「暂时在别处」。 但它没规定「方法要不要变」,历史上因此出过很多乱子。
上一节 301 永久,这一节 302 临时。 两者的差别本该只是缓存,实际却牵扯到「POST 会不会被悄悄改成 GET」。
一、302 的历史包袱
HTTP/1.0 里这个码叫 302 Moved Temporarily,标准只说「去 Location」,没说用什么方法去。
于是各家实现自己决定:多数浏览器把 POST 改成 GET,有的保持 POST 重发。
同一段代码在不同客户端行为不同,这在当年是真实的兼容性地狱。
HTTP/1.1 把这个码改名成 Found(名字更含糊了),并且加了一句「除非请求方法是 HEAD,
客户端可以改 GET」——仍然是「可以」,不是「必须」。歧义没解决。
二、补丁:303 与 307
正因为 302 说不清,后来才补了两个码把语义拆开: | 码 | 方法怎么办 | 典型用途 | |---|---|---| | 302 | 含糊(多数改 GET) | 兼容老系统 | | 303 | 强制改 GET | POST 之后看结果 | | 307 | 强制保持原方法 | API 临时迁移 | 所以「302 用在哪里」的正确答案是:新代码不该用它。要换方法就用 303,要保方法就用 307。 302 只剩一个用途:跟只认 302 的老系统打交道。这个现实在 2026 年依然存在。
三、一个真会咬人的场景
表单提交后回 302 指向结果页。浏览器把 POST 改成 GET 去请求,用户看到结果,一切正常。 但如果某个客户端保持了 POST,它会把整个表单数据重新提交一次——重复下单。 这就是为什么支付流程绝对不能用 302。 Demo 里模拟的是主流浏览器的行为:第一跳 POST,第二跳 GET,请求体丢了。
四、动手 Demo:302 之后方法变了没有
服务端收到 POST 回 302,客户端按主流浏览器行为改用 GET 跟过去, 把服务端两次收到的请求行打出来对比。
if path == u'/login':
return (u'HTTP/1.1 302 Found', [(u'Location', u'/home')], b'')
# 模拟浏览器:302 之后改成 GET
s.sendall(_req(u'GET', loc))
实跑输出:
--------------------------------------------------------------------
302 Found:临时搬家,方法可能会变
--------------------------------------------------------------------
服务端第 1 次收到 POST /login HTTP/1.1
服务端第 2 次收到 GET /home HTTP/1.1
第一跳 HTTP/1.1 302 Found
第二跳用了 GET(模拟主流浏览器的历史行为)
302 的语义 临时。不该缓存,但「方法要不要变」标准没说清
结果 POST 被悄悄变成 GET,请求体丢了
这就是 302 的历史包袱:早期实现各自为政,
有的把 POST 改 GET,有的原样重发 POST。所以后来补了 303 和 307。
今天写代码,跳转一律用 303(要换 GET)或 307(要保持方法),
302 只在兼容老系统时出现。
服务端第 1 次收到 POST /login、第 2 次收到 GET /home——方法在跳转过程中被悄悄换掉了。
结果 POST 被悄悄变成 GET,请求体丢了——如果那个请求体是订单,你就丢了一笔订单;
如果客户端保持 POST,你就下了两次单。两种都糟。
所以本节真正的结论不是「302 是什么」,而是「302 靠不住,别用」。
30 秒实验
- 把服务端改成 307,重跑,看第二次收到的是 POST 还是 GET。
- 把服务端改成 303,重跑,看结果(应该也是 GET)。
- 想一想:为什么 HTTP/1.1 把这个码的名字从「临时移动」改成「Found」?
常见误区
- 「302 就是临时版的 301」——除了缓存,方法处理也不一样。
- 「302 之后一定是 GET」——标准写的是「可以」,不是「必须」。
- 「302 和 303 一样」——303 才是强制 GET。
自测三问(点开对答案)
1. 302 的语义缺口是什么? —— 没规定跳转时用不用原方法,导致各家实现不一致。 2. 要「POST 之后跳去看结果」该用哪个码? —— 303 See Other。 3. 支付流程为什么不能用 302? —— 如果客户端保持 POST,会把支付请求重新提交一次,造成重复扣款。302 说不清方法,303 就是来把这件事说清的。
- ☐ (本节完,下一节 4.3.3 303 See Other)