如何修复 ERR_TOO_MANY_REDIRECTS

ERR_TOO_MANY_REDIRECTS 是 Chrome 在请求从一个 URL 不断跳转到另一个 URL、跳转次数过多时显示的提示,浏览器为自我保护而终止了这个无限循环。在幕后,服务器不断返回重定向响应(301、302、307 或 308),它们彼此指向对方,整条链路始终无法到达真正的页面。浏览器通常在大约 20 次跳转后停止,并显示那个熟悉的“此网页无法正常重定向”界面。

与 502 或 504 不同,这里的服务器本身完全正常 —— 它只是配置错了。本应只触发一次的重定向在每次请求时都触发了,包括它重定向到的那个目标请求。好消息是,这种循环几乎总是可以追溯到六个根本原因之一,而每一个都有明确的修复方法。本指南按照你最容易遇到的顺序,逐一讲解针对 Nginx、Apache、WordPress 和 Cloudflare 的解决方案。

重定向循环的原因

重定向循环始终源于对“URL 规范版本究竟在哪里”的分歧。两条重定向互相争执,浏览器夹在中间。导致绝大多数循环的两个争议点是协议(HTTP 与 HTTPS)和主机名(www 与非 www)。

HTTP → HTTPS 循环

最常见的现代循环发生在 CDN 与源站之间,双方都坚持使用 HTTPS。你的源站被配置为将每个 HTTP 请求重定向到 HTTPS —— 这单独看是正确的。但像 Cloudflare 这样的代理在“Flexible”SSL 模式下,即使访客使用的是 HTTPS,也会在每次请求时通过普通 HTTP 连接你的源站。于是访客访问 HTTPS,Cloudflare 通过 HTTP 询问源站,源站说“去 HTTPS”,Cloudflare 又通过 HTTP 再问一次,循环就此无限重复。源站做的没错;问题在于它看不到访客真正使用的协议。

www ↔ 非 www 循环

第二种经典循环是主机名不匹配。你的 DNS、Web 服务器、CMS 设置和 CDN 各自对 example.com 还是 www.example.com 才是规范地址有自己的看法。如果服务器把 www 重定向到非 www,但 CMS 又把每个非 www 的 URL 改写回 www,就会形成循环。当两个 server 块互相重定向到对方,或某条重定向规则忘记排除它自身的目标时,同样会发生这种情况。

常见原因

在深入分步修复之前,先用下表快速把你的情况匹配到根本原因和对应的解决章节。

原因 发生位置 修复方法
Cloudflare Flexible SSL 在源站把 HTTP 重定向到 HTTPS Cloudflare + 强制 HTTPS 的源站 将 Cloudflare SSL 模式设为 Full 或 Full (Strict)(步骤 2)
www 与非 www 互相重定向到对方 Nginx/Apache server 块、CMS 设置 选定一个规范主机名;另一个只重定向一次(步骤 3)
两条冲突的 return 301 / RewriteRule 指令 nginx.conf、Apache .htaccess curl -IL 追踪;删除冲突规则(步骤 4)
WordPress siteurl / home 指向错误的协议或主机名 wp_options 修正选项或在 wp-config.php 中硬编码(步骤 5)
强制 SSL 或缓存插件添加第二层重定向 WordPress 插件(如 Really Simple SSL) 禁用插件;让服务器处理 HTTPS(步骤 6)
过期 Cookie 把浏览器固定在旧版本 客户端浏览器 清除该域名的 Cookie;在无痕窗口测试(步骤 1)

分步修复指南

步骤 1:清除浏览器 Cookie 和缓存

在修改服务器任何东西之前,先排除客户端问题。浏览器会按域名缓存 Cookie,记录你上次使用的协议或主机名变体,而 HSTS 可能把站点固定在 HTTPS 上长达数月。一个过期的 Cookie 会让整个站点看起来都坏了,而实际上只有你的浏览器在循环。

在 Chrome 中,打开开发者工具(F12),进入 应用程序 → Cookie,选择该域名并删除所有条目。然后在一个全新的无痕窗口中测试该 URL。如果循环消失,原因就是你这台机器上缓存的重定向或 Cookie —— 无需修改服务器,不过你仍可能想找出是谁设置了那个错误的 Cookie,以免其他访客受影响。

步骤 2:检查 HTTPS/SSL 配置(Cloudflare Flexible SSL 问题)

如果站点从服务器直接访问正常,但经过 CDN 时就循环,那么 Flexible SSL 是头号嫌疑。在 Flexible 模式下,Cloudflare 终止访客的 HTTPS 连接,并通过 HTTP 与你的源站通信。如果你的源站强制 HTTP→HTTPS,每个请求都会循环。

最干净的修复方法是在源站安装一张有效证书,并将 Cloudflare 的 SSL/TLS 加密模式切换为 FullFull (Strict)。这样 Cloudflare 通过 HTTPS 连接,源站看到的是真正的 HTTPS 请求,不会触发任何重定向。如果暂时无法安装源站证书,你可以改为让源站信任 Cloudflare 发送的 X-Forwarded-Proto 头 —— 见下一节的 Nginx 配置。

步骤 3:验证 www 与非 www 一致性

选定一个规范主机名,并只在唯一一处强制执行。决定 https://example.com 还是 https://www.example.com 是规范地址,然后确保 DNS、Web 服务器、CMS 和 CDN 全部一致。最常见的错误是在服务器层重定向,同时又在 CMS 内部强制相反的主机名,于是两者互相打架。

追踪整条链路,看清每一层各自做了什么:

# 追踪两个主机名的重定向,观察它们去向哪里
curl -ILk https://example.com
curl -ILk https://www.example.com

每个主机名都应以单次 301 然后一次 200 解析到你的规范 URL。如果 www 重定向到非 www,而非 www 又重定向回 www,你就找到了循环。删除规范侧的重定向,让它直接提供内容。

步骤 4:检查 Nginx/Apache 重定向规则

冲突的重定向指令是下一个嫌疑对象。在 Nginx 中,查找互相指向的多个 return 301 语句,或位于目标 server 块内部的重定向。在 Apache 中,查看 .htaccess 中的 RewriteRule 链,看一条规则的目标是否匹配另一条规则的条件。

# 追踪完整的重定向链及头部
curl -ILk https://example.com/

按顺序阅读每个 location: 头。一旦看到某个 URL 重复出现,你就找到了那两条互相争执的规则。注释掉其中一条,重载服务器,再次测试。正确的配置应当只有一次到规范协议和主机名的重定向,而规范 server 块返回 200 且不再有进一步重定向。

步骤 5:修复 WordPress 的 siteurl 和 home 选项

WordPress 从 wp_options 表读取两个选项 —— siteurlhome —— 来知道自己的地址。如果其中任意一个包含错误的协议(例如你提供 HTTPS 时却是 http://)或错误的主机名(例如你的服务器把 www 重定向走时却是 www),WordPress 就会生成与服务器冲突的链接,站点随之循环 —— 还经常把你锁在管理后台之外。

你可以直接在数据库中修正这些值,但最快、最可靠的修复方法是在 wp-config.php 中硬编码这两个常量。这会完全覆盖数据库的值,阻止 WordPress 反复质疑服务器。具体代码见下文的 WordPress 部分。

步骤 6:禁用插件并检查 .htaccess

如果服务器和 CMS 设置看起来都正确,但循环依旧,几乎总是某个插件在添加第二层重定向。强制 SSL 插件(如 Really Simple SSL)、缓存插件和 SEO 插件都可能在服务器已有行为之上插入自己的 HTTP→HTTPS 或 www 改写。两条重定向做同一件事就足以形成循环。

要测试,可通过 FTP/SSH 临时重命名 wp-content/plugins 文件夹(这会一次性停用所有插件)。如果循环消失,逐个重新启用插件,直到它再次出现 —— 最后启用的那个就是元凶。同时,在 WordPress 中保存 设置 → 固定链接 来重新生成一份干净的 .htaccess,覆盖任何损坏的改写规则。

Nginx 重定向配置

正确的 Nginx 配置应当:HTTP 到 HTTPS 只重定向一次,非规范主机名到规范主机名只重定向一次,然后直接提供最终页面而不再重定向。当位于 Cloudflare 或任何反向代理之后时,关键是信任 X-Forwarded-Proto 头,这样源站只会在访客真正通过 HTTP 到达时才重定向。

# 一次性把 HTTP -> HTTPS(以及 www -> 非 www)
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

# 把 www HTTPS -> 非 www HTTPS
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name www.example.com;
    # ssl_certificate / ssl_certificate_key here
    return 301 https://example.com$request_uri;
}

# 规范 server 块 - 此处不要重定向
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name example.com;
    # ssl_certificate / ssl_certificate_key here

    # 位于 Cloudflare 之后:信任访客的真实协议。
    # 仅当访客确实使用 HTTP 时才重定向。
    if ($http_x_forwarded_proto = "http") {
        return 301 https://$host$request_uri;
    }

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

关键细节是 if ($http_x_forwarded_proto = "http") 这个判断。没有它,一个 Flexible-SSL 的 Cloudflare 请求(在源站以 HTTP 到达)会被重定向到 HTTPS、送回 Cloudflare、再以 HTTP 重新获取 —— 经典循环。有了它,源站只会在访客真正使用 HTTP 时才重定向,而这在正确配置的代理之后永远不会发生。

编辑后,测试并重载:

nginx -t          # 校验语法
nginx -s reload   # 不中断连接地应用

WordPress wp-config.php 修复

当 WordPress 的重定向循环把你锁在 /wp-admin 之外时,最快的恢复方法是在 wp-config.php 中硬编码站点 URL。在 /* That's all, stop editing! */ 注释之上添加下面两行,使用你的规范 https URL:

define('WP_HOME', 'https://example.com');
define('WP_SITEURL', 'https://example.com');

这两个常量会覆盖 wp_options 表中的 homesiteurl 行,因此即使数据库里仍存着错误的 http://www 值,WordPress 也会使用正确的 URL,循环随之停止。等你重新能登录后,也把数据库里的值改成一致:

UPDATE wp_options
SET option_value = 'https://example.com'
WHERE option_name IN ('siteurl', 'home');

如果你想防止以后有人通过后台界面修改 URL,就把 wp-config.php 里的 define() 行保留下来;否则在修正数据库后即可移除。

快速参考表

重定向循环可能涉及四种重定向状态码中的任何一种。理解它们的区别有助于你阅读 curl -IL 的追踪结果,并为永久与临时跳转选择正确的状态码。

状态码 名称 永久? 保留方法? 典型用途
301 Moved Permanently 否(可能把 POST 变成 GET) 永久 URL 变更、HTTP→HTTPS
302 Found 否(可能把 POST 变成 GET) 临时重定向
307 Temporary Redirect 是(保留方法和请求体) 必须保留 POST 的临时重定向
308 Permanent Redirect 是(保留方法和请求体) 必须保留 POST 的永久迁移

注意,循环并不是由使用了“错误的”状态码引起的 —— 只要两条重定向互相指向,301/302/307/308 任何一个都会循环。但 301 会被浏览器激进缓存,所以由 301 引起的循环即使在你修复服务器之后仍可能持续,直到你清除缓存的重定向。这就是为什么步骤 1(清除 Cookie 和缓存)即使根因在服务器端也依然重要。

常见问题

如何在 Chrome 中只清除某一个站点的 Cookie?

打开开发者工具(F12),进入 应用程序 → 存储 → 清除存储,或展开 Cookie 并删除该域名的条目。或者点击 URL 旁边的锁形图标,选择 Cookies and site data 并移除。然后在无痕窗口中重新加载,确认循环已消失,再去断定服务器有问题。

为什么 ERR_TOO_MANY_REDIRECTS 只在经过 Cloudflare 时发生?

因为 Cloudflare 位于访客和你的源站之间,能改变源站看到的协议。在 Flexible SSL 模式下,即使访客使用 HTTPS,Cloudflare 也通过 HTTP 连接源站。如果你的源站强制 HTTP→HTTPS,它就会重定向一个 Cloudflare 永远以 HTTP 重新发起的请求,从而形成无限循环。切换到 Full 或 Full (Strict) SSL 模式,或在源站信任 X-Forwarded-Proto 头,即可解决。

一个有问题的 .htaccess 文件会导致重定向循环吗?

会。Apache 中常见的循环是:一条 RewriteRule 在不检查请求是否已是 HTTPS 的情况下把 HTTP 重定向到 HTTPS,或是两条规则里一条把 www 重定向到非 www、另一条却反过来。把 .htaccess 重命名来测试:如果循环停止,就是这个文件的缘故。在 WordPress 中重新保存固定链接,或从备份恢复,即可重新生成一份干净的 .htaccess

无法访问 wp-admin 时,如何修复 WordPress 的重定向循环?

通过 FTP 或 SSH 编辑 wp-config.php,用你的规范 URL 添加 define('WP_HOME','https://example.com');define('WP_SITEURL','https://example.com');。这会覆盖导致循环的数据库值,通常能立即恢复访问。你也可以重命名 wp-content/plugins 文件夹来排除插件因素,登录后再修正 wp_options 中的 siteurl/home 行。

总结

ERR_TOO_MANY_REDIRECTS 从来不是随机发生的 —— 它总是两条重定向在为规范协议或主机名争执。按顺序走完这六个步骤:清除客户端 Cookie、修复 Cloudflare 的 SSL 模式、就单个 www/非 www 主机名达成一致、删除冲突的 Nginx/Apache 规则、修正 WordPress 的 siteurl/home、并禁用强制 SSL 插件。用 curl -IL 观察整条链路,一旦某个 URL 重复就停下来。几乎每种情况下,循环都能在单次配置改动后解决;一旦你的规范 server 块返回一个干净的 200,这个错误就彻底消失了。

相关指南