12k
All articles

如何修复 'ERR_TOO_MANY_REDIRECTS'

通过诊断优先指南修复ERR_TOO_MANY_REDIRECTS,排查重定向循环、HTTP到HTTPS不匹配、Cloudflare SSL和代理设置。

OpenReplay Team
OpenReplay Team
如何修复 'ERR_TOO_MANY_REDIRECTS'

ERR_TOO_MANY_REDIRECTS 表示浏览器跟随了一条永远无法解析的重定向链——最常见的情况是 URL A → URL B → URL A——在达到内置跳转上限后放弃请求。

如果你遇到了这个问题,很可能是修改了某个代理或 SSL 配置,然后发现每个请求都在两个 URL 之间来回跳转,而不是正常加载页面。这个错误令人抓狂,恰恰是因为一小时前页面还运行正常,而且表面上看不出任何问题。这不是浏览器 bug,也不是偶发性故障;它表明你的技术栈中有两条或多条重定向规则对某个 URL 的目标地址存在分歧。本指南以诊断为先:先追踪重定向循环,再修改配置,然后修复真正的根本原因——对大多数开发者来说,根本原因是代理或 CDN 后端的协议不匹配,而不是某个 WordPress 插件。

核心要点

  • ERR_TOO_MANY_REDIRECTS 是一个重定向循环;Chromium 和 Firefox 在跟随 20 次跳转后停止,Safari 停止得更早,随后显示错误而非页面内容。
  • 在修改任何配置之前先进行诊断:curl -I -L https://yourdomain.com 会打印每次跳转的响应头,循环会以连续的 Location: 行中相同的两个 URL 交替出现的形式呈现。
  • 开发者最常遇到的循环是协议不匹配:代理或 CDN 终止 TLS 后将纯 HTTP 请求转发给源站,而源站强制重定向到 HTTPS,如此循环往复。
  • 在 Cloudflare 上,Flexible SSL 模式加上”Always Use HTTPS”(或源站强制 HTTPS)必然导致循环;应在安装源站证书后切换至 Full (strict) 模式。
  • 持久性修复方案是在且仅在一个层(框架、Web 服务器或 CDN)中处理 HTTP→HTTPS 和 www/非 www 重定向,而不是在多个层同时处理。

‘ERR_TOO_MANY_REDIRECTS’ 是什么意思?

重定向循环发生在你的站点持续对某个 URL 返回重定向响应,而目标 URL 最终又指回原处,导致浏览器始终无法收到最终的 200 响应。浏览器对跟随跳转的次数有上限:Chromium 和 Firefox 均在 20 次后停止,Safari 停止得更早,此时浏览器放弃请求并显示错误。

不同浏览器的错误提示措辞各异,但以下所有提示描述的都是同一种情况:

浏览器错误信息
ChromeERR_TOO_MANY_REDIRECTS / “redirected you too many times”
Firefox”The page isn’t redirecting properly”
Edge”This page isn’t working right now”
Safari”Safari Can’t Open the Page”

重定向本身是普通的 3xx 响应,通常为 301302,每个响应都携带一个 Location 头。在循环中,相同的两个 Location 值交替出现,直到浏览器放弃为止。

诊断优先:追踪重定向链

在修改任何配置之前,先从命令行追踪重定向链。curl -I -L https://yourdomain.com 会打印每次跳转的响应头,循环会以连续的 Location: 行中相同的两个 URL 交替出现的形式呈现。在 curl 手册中,-I--head)仅获取响应头,-L--location)则跟随每个 Location 头跳转到下一个 URL。

存在循环的源站会产生如下输出:

HTTP/2 301
location: https://app.example.com/

HTTP/2 301
location: http://app.example.com/

HTTP/2 301
location: https://app.example.com/
...
curl: (47) Maximum (50) redirects followed

这里交替出现的 http://https:// 是协议不匹配循环的典型特征。如果同时需要响应体,可使用 curl -sSL -o /dev/null -D - https://yourdomain.com,该命令会输出所有响应头并丢弃响应体。

无需安装工具的替代方案:浏览器 DevTools 的 Network 标签页会显示相同的 301/302 链及每个 LocationRedirect Path 扩展或在线重定向检测工具也可以渲染某个 URL 的重定向链。无论使用哪种输出方式,找到重复出现的 URL——那对交替出现的 URL 就是循环所在。

最常见的开发者问题:SSL 终止导致的 HTTP↔HTTPS 循环

开发者最常遇到的重定向循环并非由插件引起,而是协议不匹配:代理或 CDN 终止 TLS 后将纯 HTTP 请求转发给源站,源站检测到 http 后重定向到 https,如此循环往复。浏览器与边缘节点之间使用 HTTPS 通信;边缘节点与应用之间使用 HTTP 通信;应用”好心地”重定向回 HTTPS。

在 Cloudflare 上,将 SSL/TLS 设置为 Flexible 模式,同时源站也强制 HTTPS,必然导致循环,因为 Flexible 模式始终以 HTTP 连接源站。触发点通常是在 Flexible 模式之上额外开启”Always Use HTTPS”开关。修复方法:在源站安装证书,然后将模式切换为 Full (strict)。当前可用的模式为 Off、Flexible、Full、Full (strict) 和 Strict,而非旧版教程中描述的”三种模式”。

在自建代理或负载均衡器后端,应让应用信任转发的协议头,而不是重新发起重定向。代理应发送 X-Forwarded-Proto 头,携带浏览器原始使用的协议,应用应读取该头而非明文跳转的协议。在 Express 中,启用 trust proxy,使 req.protocolreq.secure 反映转发的值:

// Trust the first proxy hop, then req.secure reflects X-Forwarded-Proto
app.set('trust proxy', 1);

app.use((req, res, next) => {
  if (!req.secure) {
    return res.redirect(301, `https://${req.headers.host}${req.originalUrl}`);
  }
  next();
});

不设置 trust proxy 时,在 TLS 终止代理后端,req.secure 始终为 false,上述中间件将陷入循环。在 Nginx 中处理源站重定向时,应根据转发的协议进行条件判断,确保规则不会对已经以 HTTPS 到达的流量再次触发:

# Only redirect when the edge saw plain HTTP
if ($http_x_forwarded_proto = "http") {
  return 301 https://$host$request_uri;
}

仅对已登录用户或仅在 CDN 后端触发的生产环境循环,对匿名 curl 请求是不可见的。对受影响会话进行会话回放,可以还原真实用户的 Cookie 和边缘上下文,呈现出那两个来回跳转的 URL,从而重现服务器日志描述但无法直接观察的问题场景。

过期 Cookie 和错误路由的认证是第二大常见原因。持有旧重定向状态的 Cookie,或浏览器缓存的 HSTS 策略,可能导致某个客户端陷入循环,而其他用户正常访问——这就是”在普通浏览器窗口中失败,在隐身模式下正常”的症状。首先清除受影响域名的 Cookie 和站点数据。

程序层面的典型案例是认证守卫在自身的登录页上陷入循环。如果 /login 本身也受”将未认证用户重定向到 /login”规则的保护,每次访问都会跳回 /login。解决方法是将登录路由排除在守卫之外。同样的问题也会发生在:登录处理器将用户重定向到受保护页面,而该页面的守卫因 Session Cookie 从未被设置而立即将用户送回——这是上述 req.secure 不匹配的常见副作用,因为 Secure Cookie 会在代理的 HTTP 跳转中被拒绝设置。

重定向规则错误:两个层之间的分歧

重定向循环几乎总是由两个层(框架、Web 服务器和 CDN 各自执行不同的规则)对规范 URL 存在分歧引起的,因此持久性修复方案是在且仅在一个层中处理 HTTP→HTTPS 和 www/非 www 重定向。典型案例:一个层强制添加 www,另一个层去掉 www;某条规则的目标地址仍然匹配自身的触发条件;或者同一条重定向在框架、主机和 CDN 中被重复配置。

框架是一个正式的重定向层,不应被忽视:

  • Next.jsnext.config.js 中通过 async redirects() 定义重定向,其中 permanent: true 发出 308false 发出 307。注意,从 Next.js 16 起,旧的 middleware 文件约定已重命名为 Proxy(proxy.ts;遗留的 middleware.ts 在 Edge 运行时场景下仍可使用,但已被弃用,将在未来版本中移除,因此其中的重定向或认证逻辑应迁移至 proxy.ts
  • Nginx 使用 return 301 指令:如上所示,应添加条件判断,防止其在代理后端重复触发。
  • Express 使用中间件;在中间件链中保持且仅保持一个 HTTPS 重定向中间件。
  • WordPress 是同一模式的一个具体实例:WordPress AddressSite Address 设置不一致,本质上就是两个层存在分歧,将两者设置为一致即可解决。

在服务器端,Apache 还可能抛出一个独特的错误:“request exceeded the limit of 10 internal redirects”,其内部重写上限为 10 次,与浏览器的 20 次跳转上限相互独立——这是一个有用的线索,表明循环存在于 .htaccess 中,而非客户端。在 Cloudflare 上,应将 HTTP→HTTPS 重定向配置在现代规则引擎Redirect Rule 中;Page Rules 正在被逐步淘汰,由现代规则引擎取代。

如何预防重定向循环?

大多数循环是由配置变更引入的,因此应谨慎进行重定向相关的修改。请参考以下检查清单:

  1. 每种重定向由且仅由一个层负责。 明确 HTTPS 和 www/非 www 规范化由 CDN、Web 服务器还是应用层处理,并从其他两个层中移除重复配置。
  2. 信任代理,不要重复重定向。 在任何 TLS 终止节点后端,读取 X-Forwarded-Proto 而不是盲目强制 HTTPS。
  3. 每次修改后重新追踪重定向链。 在任何 HTTPS、域名或 URL 结构变更后,对受影响的 URL 运行 curl -I -L,确认最终以单个 200 结束。

走出重定向循环最快的路径永远是追踪,而不是猜测。对失败的 URL 运行 curl -I -L,在 Location 响应头中找到来回跳转的两个 URL,然后修复那个”逆流而上”发起重定向的层——最常见的情况是代理将 HTTP 流量转发给坚持使用 HTTPS 的源站。

常见问题

为什么重定向循环在隐身模式下消失,但在普通浏览器窗口中依然存在?

隐身模式启动时不携带任何已存储的 Cookie 或缓存的 HSTS 策略,因此在普通浏览中出现循环、在隐私窗口中消失,指向的是客户端状态问题,而非服务器规则。持有旧重定向状态的过期 Cookie,或强制对配置错误的源站使用 HTTPS 的缓存 HSTS 策略,只会让受影响的浏览器配置文件陷入循环,而其他用户可以正常访问站点。清除该域名的 Cookie 和站点数据;如果怀疑是 HSTS 问题,请检查 chrome://net-internals/#hsts。

Cloudflare Flexible 和 Full (strict) SSL 模式在重定向循环方面有何区别?

Flexible 模式始终以纯 HTTP 从 Cloudflare 连接源站,因此如果源站强制将 HTTP 重定向到 HTTPS,请求将永远循环。Full (strict) 模式以 HTTPS 连接源站并验证受信任的证书,与源站的预期一致,从而打破循环。请先在源站安装有效证书,再将 SSL/TLS 模式从 Flexible 切换为 Full (strict)。在 Flexible 模式上叠加“Always Use HTTPS”开关是常见的触发因素。

为什么 curl 和浏览器对同一 URL 显示不同的重定向行为?

curl 以匿名客户端身份运行,不携带 Cookie、缓存的 HSTS 策略或登录会话,因此它只能重现由服务器或 CDN 规则引起的、对所有请求均生效的循环。依赖特定 Cookie、已认证会话或特定 CDN 边缘节点的循环不会在匿名 curl 追踪中出现。对于这类情况,需要捕获真实用户的上下文:在受影响的会话上使用浏览器 DevTools,或通过会话回放查看在该用户的 Cookie 和认证状态下哪两个 URL 在来回跳转。

Next.js 16 中在 next.config.js 定义的重定向是否适用于通过 Link 或 router.push 进行的客户端导航?

使用 Pages Router 时,在 next.config.js 的 redirects() 函数中定义的重定向,除非存在 Proxy 文件(原 middleware)且匹配该路径,否则不会应用于通过 Link 或 router.push 进行的客户端路由。next.config.js 中的重定向仅在服务器端对完整页面加载和初始请求生效,因此客户端路由切换可以绕过它们。在 Next.js 16 中,middleware 文件约定已重命名为 Proxy(proxy.ts);遗留的 middleware.ts 应当迁移,因为其中遗留的重定向和认证逻辑可能会停止运行。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.