# ERR_TOO_MANY_REDIRECTS：Cloudflare Flexible 导致的重定向循环

> Cloudflare 用 Flexible 模式、源站又强制跳 HTTPS，就会出现“将您重定向的次数过多”。怎么确认是这个原因，以及三种修法。

Source: https://ssl.sh/zh/errors/cloudflare-flexible-ssl-too-many-redirects/
Last updated: 2026-11-10

**套了 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 请求，所以它对每个请求都回同一个跳转。

```text
浏览器 --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](https://ssl.sh/zh/guides/aliyun-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` 反复出现，就是循环。先看跳转链：

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

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

```bash
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 证书](https://ssl.sh/zh/guides/cloudflare-origin-certificate/)、Let's Encrypt，或者阿里云、腾讯云的免费证书都行），然后把加密模式改成 Full (strict)。Cloudflare 改用 HTTPS 回源后，源站的强制跳转就不会再循环。

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

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

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

Apache：

```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 教程](https://ssl.sh/zh/guides/redirect-http-to-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 检测](https://ssl.sh/zh/tools/hsts-checker/) 看一下；开启后浏览器不再允许用 HTTP 访问这个域名，由此引起的问题见 [HSTS 报错](https://ssl.sh/zh/errors/hsts-cannot-bypass/)。
