301、302、303、307 和 308 到底有什么区别
重定向是带有 Location 头的 3xx 响应。这些状态码的区别在于两点:迁移是否为永久性的,以及客户端是否可以改变请求方法。具体定义见 RFC 9110。
- 301 Moved Permanently:永久。出于历史原因,客户端在跟随重定向时可以把 POST 改为 GET,而浏览器确实会这样做。
- 302 Found:临时,同样存在把 POST 改为 GET 的历史例外。
- 303 See Other:要求客户端用 GET(或 HEAD)获取目标资源。通常用于表单提交之后显示结果页面。
- 307 Temporary Redirect:临时,且请求方法和请求体不得改变。
- 308 Permanent Redirect:永久,且请求方法和请求体不得改变。
301 和 308 默认可缓存,因此浏览器可能直接复用它们而不再询问服务器;302 和 307 只有在响应带有明确的缓存头时才会被缓存。
用 curl 追踪重定向链
curl -sIL 会跟随每一次重定向,并输出每个响应的头部:
curl -sIL http://example.com/ | grep -iE '^(HTTP|location)'两跳重定向链的典型输出如下:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/
HTTP/2 301
location: https://www.example.com/
HTTP/2 200-I 发送的是 HEAD 请求,而有些服务器对 HEAD 和 GET 的响应不同。下面第一条命令用 GET 追踪并只输出头部;第二条输出一行摘要,包含跳转次数和最终 URL:
curl -sL -o /dev/null -D - https://example.com/
curl -sL -o /dev/null -w '%{num_redirects} hops, final: %{http_code} %{url_effective}\n' http://example.com/每个入口都要测试,因为它们可能走不同的路径:
for u in http://example.com/ http://www.example.com/ https://example.com/ https://www.example.com/; do
echo "== $u"; curl -sIL --max-redirs 10 "$u" | grep -iE '^(HTTP|location)'
donecurl 只跟随 Location 头。它不执行 JavaScript 或 meta refresh,默认情况下也不会在多次运行之间保留 HSTS 或重定向缓存,因此很适合作为基准,与浏览器中看到的结果进行对比。
在浏览器开发者工具中追踪
在 Network(网络)面板中启用 Preserve log(Chrome、Edge)或 Persist Logs(Firefox),这样页面跳转时列表不会被清空;再勾选 Disable cache,然后加载起始 URL。筛选文档类请求后,每一跳都会显示为一行,并带有状态码和 Location 响应头。
留意那些根本没有到达服务器的条目。Chrome 会把从缓存中复用的重定向标记为“from disk cache”,并把 HSTS 升级显示为 307 Internal Redirect,同时带有 Non-Authoritative-Reason: HSTS 头。这两者都是浏览器自己生成的,而不是你的服务器返回的。
常见问题
- 重定向循环:A 重定向到 B,B 又重定向回 A,直到浏览器报出
ERR_TOO_MANY_REDIRECTS之类的错误。常见原因是 CDN 或负载均衡器终止了 HTTPS,再通过 HTTP 访问源站,而源站不知道原始协议(例如因为它忽略了X-Forwarded-Proto),于是不断重定向到 HTTPS。 - 跳转次数过多:多年来不断添加的规则层层叠加,例如
http://example.com/page→https://example.com/page→https://www.example.com/page→https://www.example.com/page/。每多一跳就多一次往返。应让每条规则都直接指向最终 URL。 - http → https → www 链:通常值得合并为一跳,但有一个例外。如果你希望裸域也启用 HSTS,或计划将其提交到 HSTS 预加载列表,
http://example.com应先重定向到同一主机上的https://example.com,这样浏览器才能在那里收到 HSTS 头。 - 规范信号不一致:重定向把用户带到
www,而rel="canonical"、内部链接或站点地图仍在使用裸域、HTTP 或另一种末尾斜杠形式。 - 301、302 或 303 导致方法改变:发往旧端点的 POST 在重定向后变成 GET,请求体被丢弃,表单或 API 调用随之失败。需要保留请求方法时,请使用 307 或 308。
- 缓存的重定向与 HSTS:浏览器一旦收到
Strict-Transport-Security头,就会在max-age到期之前自行把http://改写为https://,不发出任何网络请求。缓存的 301 也会以类似方式被复用。修复服务器并不会改变该浏览器中看到的结果,因此请用 curl 或全新的浏览器配置文件进行测试。在 Chrome 中,可以在chrome://net-internals/#hsts删除动态获得的 HSTS 条目,但预加载的条目无法删除。 - meta refresh 和 JavaScript 重定向:服务器返回 200,随后页面通过
<meta http-equiv="refresh" content="0; url=/new">或window.location跳走。基于响应头的工具看不到这类重定向。可以用curl -s https://example.com/ | grep -iE 'http-equiv|location\.(href|replace)'查找它们,并尽可能改为服务器端重定向。
对 SEO 的影响
- 把重定向链控制在一跳以内。每多一跳都会增加用户和爬虫的延迟,而且爬虫在跟随一定次数的跳转后就会停止。
- 永久迁移使用永久重定向(301 或 308)。临时状态码会告诉搜索引擎原 URL 可能还会恢复。
- 让重定向目标成为规范 URL:它应返回 200,带有指向自身的
rel="canonical",并且是内部链接、站点地图和 hreflang 标注中使用的 URL。 - 把旧 URL 重定向到最接近的对应页面,而不是全部指向首页;迁移完成后也要长期保留这些重定向。
- 优先使用服务器端重定向。meta refresh 和 JavaScript 重定向只有在页面被获取(对于 JavaScript 还需渲染)之后才会生效,而且并非所有爬虫都能处理。
快速检查清单
- 追踪全部四种变体:
http与https,裸域与www。 - 每种变体都在一跳内到达最终 URL(如果有意保留 HSTS 那一步,则为两跳)。
- 永久迁移使用 301 或 308;接收 POST 的端点使用 307 或 308。
- 最终 URL 返回 200,且规范标签指向自身。
- 内部链接、站点地图和规范标签都使用最终 URL。
- CDN 或代理之后不存在循环:用 curl 测试,而不只是用浏览器。
- 页面正文中没有 meta refresh 或 JavaScript 重定向。
- 在全新的浏览器配置文件中重新测试,排除缓存的重定向和 HSTS 的影响。
用 WebTraceCheck 检查
如果不想安装任何工具就检查重定向链,可以在 WebTraceCheck 上发起网站检查,无需注册账号。该审计会检查 HTTP 响应、重定向、DNS 和技术 SEO,列出每条观察结果,并注明其来源和测量局限。如果需要查看真实浏览器的行为,可以使用 GitHub 登录并打开一个临时云浏览器,自行选择浏览器引擎、地区和设备模式;它独立于你日常使用、已积累了缓存重定向和 HSTS 条目的浏览器。