平台和 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,就说明循环已经解开。
常见问题
Chrome 提示“尝试删除您的 Cookie”,有用吗?
对 Flexible 造成的循环没用。循环是服务器对每个请求都回同一个跳转造成的,跟 Cookie 无关。只有当某个应用根据 Cookie 决定跳转时,删 Cookie 才可能有效。
以前好好的,在宝塔里打开“强制 HTTPS”之后就打不开了,为什么?
打开之前,源站收到 Cloudflare 的 HTTP 请求会直接返回网页;打开之后,源站对所有 HTTP 请求都回 301 跳转,而 Flexible 模式下 Cloudflare 永远用 HTTP 回源,于是每次拿到的都是跳转。
循环解决了,Flexible 模式够安全吗?
不够安全。访客能看到小锁,但 Cloudflare 到源站这一段是明文,路径上的任何人都能看到甚至篡改内容。给源站装一张证书(免费的 Cloudflare Origin CA 证书就够),改用 Full (strict)。