站点换了域名,运营同学随手配了一条 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 与重定向缓存是独立机制,排查时分别确认