HTTP 重定向的五个状态码
路由匹配到一条请求后,handler 不一定要返回内容——它也可以回一个 3xx 状态码 + 一个 Location 响应头,意思是「要找的东西不在这个地址,去那边」。客户端读到后会自动再发一次请求去 Location。这就是重定向:服务端只改
Location,就能把同一个入口指向任意新目标。
五个重定向码看着像近义词,实际只差两个正交维度:永久还是临时(决定能不能被浏览器缓存)、跳过去时方法保不保持(POST 保持 POST,还是被悄悄改成 GET)。
200。可选状态码与原始方法观察差异。永久的那两个(301 与 308)意思是「这个地址已停用,以后都走新的」,浏览器会缓存这条跳转,下次直接去新址、连旧址都不请求,因此配错很难收回。临时的三个(302、303、307)意思是「暂时去那边,旧址还作数」,默认不缓存。
方法这一维,307 与 308 严格保持方法与 body;301 与 302 则不确定——规范原本不打算允许改方法,但现存的一些 user agent 确实会把非 GET 改成 GET,307 与 308 正是为消除这个歧义而添加的 [1]。303 则是明确要求改:GET 不变,其余方法一律改成 GET 且丢弃 body。
| 状态码 | 永久? | 方法 | 典型用途 |
|---|---|---|---|
301 Moved Permanently |
永久,可缓存 | 可能被改 GET | 永久搬家:旧 URL 不再用,把权重与书签迁到新址 |
302 Found |
临时 | 可能被改 GET | 临时跳转,最常用也最常被误用(多数场景想要的是 307 或 303) |
303 See Other |
临时 | 强制改 GET | POST 之后跳到一个 GET 结果页,即 PRG 模式防重复提交 |
307 Temporary Redirect |
临时 | 原样保持 | 临时跳转且严格保持方法与 body |
308 Permanent Redirect |
永久,可缓存 | 原样保持 | 永久跳转且严格保持方法与 body,API 端点搬迁的首选 |
想「临时跳又别改方法」用 307,想「永久跳又别改方法」用 308,POST 提交后跳到结果页防重复提交用 303。
1 · 把重定向当可配置的跳转链接
对外只暴露一个稳定端点 /api/users,而后端实现在不断升级(v1 到 v2 再到 v3)。做法是让 /api/users 回一个 308,Location 指向当前版本。需求一变,服务端改一行 Location 配置即可把所有调用方导到新版本,而调用方代码里那个
/api/users 不用改。
为什么是 308 而不是 301:API 调用常是 POST 或 PUT,308 严格保持方法与 body,POST /api/users 会原样变成 POST /api/v2/users;若用 301,POST 可能被某些客户端改成 GET,请求体丢失、语义全错。
警示 · 重定向不是免费的,每一跳都是一次完整往返。链路越长越慢,还可能绕成循环。服务器有时能自己发现循环并回 500,但跨多台服务器的循环各方都看不到全貌,只能由浏览器兜底检测并报错;Chrome 与 Firefox 的跳数上限都是 20。能在服务端内部 rewrite 解决的就别让客户端多跑一趟外部
redirect。另外 301 与 308 一旦被缓存就很难纠正,上线前务必想清楚这真的是永久吗。
回 3xx 的 handler 同样要先由匹配引擎把请求 path 命中到它才有机会执行;分享地址统一入口命中后用的正是 302,因为分享目标常变。
2 · 参考文献
- MDN Web Docs. Redirections in HTTP(五个 3xx 码的方法处理与缓存语义、重定向循环). Mozilla.(核对于 2026-08)