平台和 DevOps

ERR_TOO_MANY_REDIRECTS:Cloudflare Flexible 导致的重定向循环

作者: · 最后核实

报错信息ERR_TOO_MANY_REDIRECTS

套了 Cloudflare 的网站报 ERR_TOO_MANY_REDIRECTS,几乎都是 Cloudflare 和源站之间形成了跳转循环,最常见的诱因是 Flexible 模式。 Flexible 模式下 Cloudflare 用明文 HTTP 请求源站,源站如果强制跳 HTTPS,就会回 301;浏览器跟着跳回 Cloudflare,Cloudflare 再用 HTTP 去问源站,如此循环,直到浏览器放弃。

各客户端看到的提示

各客户端的提示文字不同,含义都是跳转次数超过了上限。

客户端 提示
Chrome / Edge / 360 极速模式 / QQ 浏览器 “该网页无法正常运作”“example.com 将您重定向的次数过多。”ERR_TOO_MANY_REDIRECTS
Firefox “此页面不能正确地重定向”
curl(加 -L) curl: (47) Maximum (50) redirects followed

Chrome 还会建议“尝试删除您的 Cookie”,对这种情况基本没用。

循环是怎么形成的

Flexible 模式下源站只收到 HTTP 请求,所以它对每个请求都回同一个跳转。

浏览器 --HTTPS--> Cloudflare --HTTP--> 源站
                                      301 Location: https://example.com/
浏览器 --HTTPS--> Cloudflare --HTTP--> 源站
                                      301 Location: https://example.com/
……

源站无法知道访客已经在用 HTTPS。Cloudflare 文档写明:源站如果通过跳转强制 HTTPS,就不要用 Flexible 模式。

国内 CDN 有同样的问题:阿里云、腾讯云 CDN 设成 HTTP 回源,源站又开了强制跳转,同样会循环,修法相同,见 阿里云 CDN 配置 HTTPS。

为什么这么多网站停留在 Flexible

Flexible 本来是给完全不支持 HTTPS 的源站准备的。很多站点在接入 Cloudflare 时顺手选了它,后来源站装上了证书,宝塔或某个 WordPress 插件又加上了“强制 HTTPS”,循环就从打开开关的那一刻开始。Cloudflare 现在给站点的默认设置是 Automatic SSL/TLS,会探测源站能力并自动选用最安全的模式;但手动选过模式的站点会一直保持原来的选择。动服务器配置之前,先到后台确认当前用的是哪个模式。

访客能做什么

访客几乎做不了什么,因为循环发生在服务器之间,换浏览器、清缓存、换网络都一样。能做的只是告诉站长“网站报重定向次数过多”,并附上出错的网址。站长自己排查时,记得用无痕窗口或命令行测试,浏览器有时会缓存 301 跳转,导致修好了还以为没修好。

常见原因(按出现频率排序)

重定向循环有 4 种原因,最常见的是 Flexible 模式加上源站强制跳 HTTPS。

1. Flexible 模式 + 源站强制跳 HTTPS

源站上有类似 Nginx 的 return 301 https://$host$request_uri;、.htaccess 里的 RewriteRule、宝塔或其他面板的“强制 HTTPS”开关,或者框架里“非安全请求一律跳转”的设置。

2. 应用程序以为请求不安全

WordPress 开了 FORCE_SSL_ADMIN,或者站点地址填的是 https://,在 Flexible 模式下收到 HTTP 请求就会跳到 HTTPS。其他按请求协议拼接绝对地址的框架也一样。

3. Full / Full (strict) 模式下源站把 HTTPS 跳回 HTTP

方向相反的循环:Cloudflare 用 HTTPS 回源,源站上某条老规则把 HTTPS 跳回 HTTP,Cloudflare 又把访客升级回 HTTPS。Cloudflare 的排查文档把这种情况列在 Full、Full (strict)、Always Use HTTPS 和 HSTS 几项下面。

4. www 和主域名互相跳

Cloudflare 的重定向规则把 www.example.com 跳到 example.com,源站却把 example.com 跳到 www.example.com。单看每一边都没错,合在一起就是死循环。

怎么确认原因

跳转链里同一个 Location 反复出现,就是循环。先看跳转链:

curl -sIL --max-redirs 6 https://example.com/ | grep -iE '^(HTTP|location|server)'

再绕过 Cloudflare 直接用 HTTP 问源站:

curl -sI --resolve example.com:80:203.0.113.10 http://example.com/ | grep -iE '^(HTTP|location)'

源站返回 301 跳到 https://example.com/,同时 Cloudflare 后台“SSL/TLS > 概述”里显示的是 Flexible,就可以确认是第 1 种原因。

怎么修

推荐的修法是给源站装证书、改用 Full (strict);暂时做不到时,把跳转交给 Cloudflare,或者让源站按 X-Forwarded-Proto 判断。

方案 A(推荐):给源站加密,改用 Full (strict)。 在源站装一张证书(免费的 Cloudflare Origin CA 证书、Let's Encrypt,或者阿里云、腾讯云的免费证书都行),然后把加密模式改成 Full (strict)。Cloudflare 改用 HTTPS 回源后,源站的强制跳转就不会再循环。

方案 B:在 Cloudflare 跳转,不在源站跳转。 暂时保留 Flexible,关掉源站(包括宝塔“强制 HTTPS”)的跳转,在 Cloudflare 打开 Always Use HTTPS,让边缘节点负责跳转。

方案 C:让源站按访客实际协议决定是否跳转。 Cloudflare 回源时会带上 X-Forwarded-Proto,值是访客连接 Cloudflare 时用的协议。Nginx:

if ($http_x_forwarded_proto = "http") {
    return 301 https://$host$request_uri;
}

Apache:

RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} =http
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

WordPress 官方文档给出的做法是在 wp-config.php 里判断 HTTP_X_FORWARDED_PROTO 包含 https 时设置 $_SERVER['HTTPS'] = 'on'。只有源站只接受 Cloudflare 的连接时,才能信任这个头,否则任何人都能伪造。

第 3 种原因,删掉源站上 HTTPS 跳 HTTP 的规则;第 4 种原因,确定唯一的规范域名,只在一个地方做跳转。通用写法见 HTTP 跳转 HTTPS 教程。

WordPress 站点的额外检查

宝塔上的 WordPress 站点,循环往往来自三处叠加:宝塔的“强制 HTTPS”、WordPress 后台“站点地址”填了 https://、再加一个 SSL 类插件。按方案 A 改成 Full (strict) 后这三处都可以保留;如果暂时只能用 Flexible,就按方案 C 在 wp-config.php 里根据 X-Forwarded-Proto 设置 HTTPS 标记,并停用会强制跳转的插件,否则后台登录页会单独陷入循环。

怎么确认已经修好

HTTPS 请求最多一次跳转就返回 200,就说明循环已经解开。

  1. curl -sIL https://example.com/ 最多一次跳转后以 200 结束。
  2. curl -sI http://example.com/ 只返回一个跳到 HTTPS 的 301。
  3. 用无痕窗口打开网站,避免浏览器缓存的旧跳转干扰判断。手机、电脑、不同浏览器各试一次,确认不是只在某一个环境下才正常。
  4. 改动过程中开启了 HSTS 的,用 HSTS 检测 看一下;开启后浏览器不再允许用 HTTP 访问这个域名,由此引起的问题见 HSTS 报错。

常见问题

Chrome 提示“尝试删除您的 Cookie”,有用吗?

对 Flexible 造成的循环没用。循环是服务器对每个请求都回同一个跳转造成的,跟 Cookie 无关。只有当某个应用根据 Cookie 决定跳转时,删 Cookie 才可能有效。

以前好好的,在宝塔里打开“强制 HTTPS”之后就打不开了,为什么?

打开之前,源站收到 Cloudflare 的 HTTP 请求会直接返回网页;打开之后,源站对所有 HTTP 请求都回 301 跳转,而 Flexible 模式下 Cloudflare 永远用 HTTP 回源,于是每次拿到的都是跳转。

循环解决了,Flexible 模式够安全吗?

不够安全。访客能看到小锁,但 Cloudflare 到源站这一段是明文,路径上的任何人都能看到甚至篡改内容。给源站装一张证书(免费的 Cloudflare Origin CA 证书就够),改用 Full (strict)。

改成 Full (strict) 后又出现 525 或 526 怎么办?

说明源站没有可用的 TLS(525)或者证书无效(526)。先把源站证书装好、装对,再切换加密模式。

资料来源

  1. Cloudflare: ERR_TOO_MANY_REDIRECTS
  2. Cloudflare: Flexible 模式
  3. Cloudflare HTTP 请求头(X-Forwarded-Proto、CF-Visitor)
  4. Chromium net_error_list.h
  5. WordPress 高级管理:HTTPS