重定向该选 301 还是 307

工具相关 ·

站点换了域名,运营同学随手配了一条 302 跳转,想着「先临时跳着,回头再改」。半年后改完域名才发现,搜索引擎收录的仍然全是老域名,外部链接权重也没转移过来。重定向的选型看似小事,选错却要花很长时间收拾。

永久与临时的区别不只是时间长短

301 Moved Permanently 表示永久重定向,308 Permanent Redirect 同样是永久,区别在后面会讲。302 Found 表示临时重定向,307 Temporary Redirect 同样是临时。

「永久」和「临时」这两个词在 HTTP 里是有实际后果的。浏览器会把永久重定向的结果长期缓存,搜索引擎会把权重和收录迁移到新地址;而临时重定向不会被长期缓存,搜索引擎继续使用原地址。

所以选型的第一步不是技术细节,而是问自己:这个地址变化是长期的还是短暂的?域名迁移、URL 结构调整、HTTP 升级到 HTTPS 属于长期变化,用 301;活动页面、A/B 测试、灰度切换、维护期跳转属于短暂变化,用 302。

301 最大的风险是「很难撤销」。用户浏览器里已经缓存了这条规则,即使你在服务器上删掉配置,老用户仍然会被带到旧地址,只能等缓存自然过期。调试重定向时如果怀疑是缓存干扰,用 curl -I 看响应头,或者换一个从未访问过的客户端测试。

方法保持:302 的历史包袱

真正让 302 变得棘手的,是历史实现上的一个问题。

HTTP 规范早期的写法里,302 的描述允许客户端在跳转时把 POST 改成 GET。这个「允许」被大量实现当成了「必须」,于是用 POST 提交表单后收到 302,浏览器会以 GET 重新请求新地址,请求体丢失,参数全部消失。

为了解决这个歧义,规范后续补充了两个语义明确的码:

303 See Other 明确要求客户端用 GET 请求新地址,专门用于表单提交后跳转,也就是常说的 PRG(Post-Redirect-Get)模式,避免用户刷新页面时重复提交。

307 Temporary Redirect 明确要求保持原请求方法不变。POST 仍然是 POST,请求体也会原样带过去。

308 Permanent Redirect 则是 307 的永久版本,既保持方法不变,又表示这是长期变化。

四个常用码怎么选

状态码是否永久是否保持方法典型场景
301永久历史上常被改成 GET域名迁移、HTTPS 升级、URL 规范化
302临时历史上常被改成 GET活动页跳转、灰度、维护期
303临时强制改成 GET表单提交后跳转(PRG)
307临时保持原方法需要保留 POST 的临时跳转
308永久保持原方法API 版本迁移、保持方法的永久迁移

这张表可以直接当决策树用:先判断是永久还是临时,再判断是否需要保持请求方法,两个维度交叉就能定位到具体的码。

什么时候必须用 307 或 308

有几类场景如果用了 301 或 302,故障会很隐蔽。

一类是接口迁移。客户端用 POST 提交数据,服务端返回 302 把请求导到新域名,客户端跟随跳转时按历史行为改成了 GET,服务端收到一个没有请求体的 GET,返回 400 或者写入一条空数据。这类问题在日志里表现为「偶尔出现空提交」,很难第一眼联想到重定向。

另一类是带签名的请求。签名通常覆盖请求方法、路径和请求体,跳转时方法一变,签名立刻失效,服务端返回 401 或签名错误。

还有一类是客户端不跟随重定向的场景。不少 HTTP 客户端默认不自动跟随跳转,只把 3xx 原样返回给调用方,这时调用方需要自己处理,选错码会让逻辑更复杂。

缓存之外还要注意的两件事

第一件是重定向链。A 跳 B、B 跳 C、C 又跳回 A,浏览器会报「重定向次数过多」,搜索引擎也会放弃抓取。迁移完成后应该检查是否存在多级跳转,尽量压成一级。

第二件是 HTTPS 与 HSTS。如果站点启用了 HSTS,浏览器会在有效期内强制使用 HTTPS,即使你把 301 规则删掉也一样。调试这类问题时,记得 HSTS 的状态和重定向缓存是两套独立机制。

排查与配置清单

  • 先定性质:长期变化用 301 或 308,短暂变化用 302 或 307
  • 涉及 POST 或带请求体的接口迁移,一律用 307 或 308,不要用 301、302
  • 表单提交后跳转用 303,遵循 PRG 模式避免重复提交
  • 301 上线前用 curl -I 确认目标地址和响应头,避免缓存后难撤销
  • 迁移完成后检查是否存在多级重定向链
  • 记住 HSTS 与重定向缓存是独立机制,排查时分别确认
阅读 13