Cloudflare 重定向次数过多怎么办:SSL 模式选错导致的循环与修复

博客 / 边缘与 CDN

Cloudflare 重定向次数过多怎么办:SSL 模式选错导致的循环与修复

站点刚接入 Cloudflare,浏览器就报“重定向次数过多”(ERR_TOO_MANY_REDIRECTS),最常见的原因是 SSL/TLS 加密模式设成了 Flexible,而源站自己又在把 HTTP 跳到 HTTPS。临时的修法是把模式改成 Full;长期应该给源站装上证书,改用 Full (strict)。如果改了模式还在循环,就用下面的 curl 命令看清 Location 链,看是哪一跳在兜圈子。

Flexible、Full、Full (strict) 到底差在哪?

Cloudflare 代理(橙色云朵)开启后,一次访问分成两段连接:浏览器到 Cloudflare 边缘,边缘到你的源站。SSL 模式只管第二段怎么走。

浏览器 ──(1)──> Cloudflare 边缘 ──(2)──> 源站
模式第 (2) 段协议是否校验源站证书适用情况
OffHTTP不涉及不建议使用
Flexible始终 HTTP(80 端口)不涉及源站完全没有 HTTPS 时的过渡
Full跟随访客协议,访客走 HTTPS 时用 HTTPS(443)不校验,自签名或过期证书也能通源站有证书但还不合规时的过渡
Full (strict)同 Full校验:证书须由公共 CA 或 Cloudflare Origin CA 签发、未过期、域名匹配推荐的长期配置

较新接入的域名可能默认是“自动”模式,Cloudflare 会探测源站能力后替你选。具体选项名称以控制台 SSL/TLS → Overview 页面为准。

为什么 Flexible 会造成无限重定向?

把上表的 Flexible 那一行代入请求路径,循环就清楚了:

  1. 浏览器请求 https://example.com/,第 (1) 段是 HTTPS。
  2. Flexible 模式下,Cloudflare 用 HTTP 去请求源站 http://example.com/。
  3. 源站的 Nginx 或应用配置了“HTTP 一律 301 到 HTTPS”,于是回一个 Location: https://example.com/。
  4. Cloudflare 把这个 301 原样交给浏览器,浏览器再请求 https://example.com/,回到第 1 步。

源站看到的永远是 HTTP,所以它会一直跳;浏览器看到的永远是 HTTPS,所以它会一直跟。Chrome 和 Firefox 默认在跟了 20 次左右后放弃,报出这个错误。

同样的结构还会以别的形式出现:源站把 HTTPS 跳回 HTTP,而 Cloudflare 又开着 Always Use HTTPS;或者重定向规则让 www 跳到裸域,源站又让裸域跳回 www。共同点是两个环节对“正确的地址”意见不一致。

第一步:用 curl 看清 Location 链

浏览器会缓存 301,排查时先别用它,直接用 curl。下面的命令跟随跳转,只打印每一跳的状态码和关键头(-S 让 curl 在静默模式下仍输出错误信息):

curl -sSIL --max-redirs 8 https://example.com/ \
  | grep -iE '^(HTTP/|location:|server:|cf-cache-status:|x-powered-by:)'

一段典型的 Flexible 循环输出长这样(示例):

HTTP/2 301
server: cloudflare
location: https://example.com/
x-powered-by: PHP/8.x
HTTP/2 301
server: cloudflare
location: https://example.com/
...
curl: (47) Maximum (8) redirects followed

怎么读:

  • Location 指向自己:请求 https://example.com/ 却被要求跳到 https://example.com/,基本可以断定是协议不一致,优先查 SSL 模式。
  • 两个地址来回切换(www 和裸域,或 http 和 https):是两条规则互相打架,下面第三节逐个查。
  • 跳转出自谁:经过 Cloudflare 的响应 server 头都显示 cloudflare,不能靠它区分。如果同时带着源站才有的头(如 x-powered-by、应用自定义头),说明 301 是源站发的;只有 Cloudflare 的头而没有源站痕迹,多半是 Always Use HTTPS、Redirect Rules 或 Page Rules 发的。
  • cf-cache-status: HIT:301 被边缘缓存了,改完配置要去 Caching → Configuration 清缓存,否则看起来“没生效”。缓存规则可以参考 Cache-Control 缓存头怎么配。

第二步:绕过 Cloudflare 直接问源站

用 --resolve 把域名指到源站 IP,就能看到源站在 HTTP 和 HTTPS 两种情况下各自怎么回应。把 203.0.113.10 换成你的源站地址:

# 模拟 Flexible:Cloudflare 用 HTTP 回源
curl -sI --resolve example.com:80:203.0.113.10 http://example.com/

# 模拟 Full:Cloudflare 用 HTTPS 回源(-k 忽略证书,只看跳转)
curl -skI --resolve example.com:443:203.0.113.10 https://example.com/

# 检查源站证书能否通过 Full (strict) 的校验(去掉 -k)
curl -sI --resolve example.com:443:203.0.113.10 https://example.com/

判断方法:

第一条结果第二条结果结论
301 到 https200源站有 HTTPS,改成 Full 或 Full (strict) 即可
301 到 https连接失败源站 443 没监听,先装证书,或临时关掉源站的强制跳转
200301 到 http源站把 HTTPS 往回跳,配合 Always Use HTTPS 会循环,去掉源站这条规则
200200源站没问题,去查 Cloudflare 侧的规则

第三条命令如果报证书错误,说明源站证书不是公共 CA 签发的,或已过期、域名不匹配,这时开 Full (strict) 会得到 526 错误,需要先换证书。例外是 Cloudflare Origin CA 证书:它不被 curl 默认信任,第三条命令同样会报错,要用 --cacert 指定 Cloudflare 官方文档提供的 Origin CA 根证书再测(见后文)。Cloudflare 边缘返回的其他 5xx,例如 502、524,可以对照 大模型 API 错误码速查。

如果 --resolve 不确定该填哪个 IP,或怀疑本地解析被改过,先按 DNS 污染和 DNS 劫持的判断方法 确认解析结果。

第三步:五个常见成因逐项检查

1. SSL 模式是 Flexible,源站又强制 HTTPS

最常见。控制台 SSL/TLS → Overview 改成 Full,清缓存,再跑一遍第一步的 curl。源站有有效证书就直接用 Full (strict)。

2. Always Use HTTPS 和源站的反向跳转冲突

SSL/TLS → Edge Certificates 里的 Always Use HTTPS 会在边缘把 HTTP 请求 301 到 HTTPS。如果源站有一条“HTTPS 跳 HTTP”的旧规则(常见于早年为了省证书写的配置),两边会来回推。删掉源站那条规则。

3. 源站自己的 301 判断依据不对

Nginx 里常见的写法是在 80 端口的 server 块里 return 301 https://$host$request_uri;。这在 Full 模式下没问题,因为 HTTPS 访客对应的回源请求走 443。但如果源站前面还有一层反向代理,按 $scheme 判断协议就会出错。经过 Cloudflare 的请求会带 X-Forwarded-Proto 和 CF-Visitor: {"scheme":"https"},应用层应读这两个头来判断访客协议,或者干脆让 Cloudflare 负责跳转,源站不再跳。

4. WordPress 的 siteurl 和 home

WordPress 会把请求跳到数据库里登记的地址。Flexible 模式下它收到的是 HTTP 请求,is_ssl() 返回 false,于是不断把用户送回 HTTPS。先查当前值(需要服务器上装有 WP-CLI):

wp option get siteurl
wp option get home

两者都应该是 https:// 开头,并且与你实际使用的主机名(带不带 www)一致。最省事的修法仍然是切到 Full (strict),让 WordPress 真正收到 HTTPS 请求。如果暂时做不到,可以在 wp-config.php 里 require_once ABSPATH . 'wp-settings.php'; 之前加上:

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

这只是过渡办法,源站依旧是明文回源。

5. www 与非 www 规则互相指向

例如 Cloudflare 的 Redirect Rules 把 www.example.com 跳到 example.com,而 Nginx 的 server_name 配置或 CMS 的站点地址又把裸域跳回 www。curl 输出里两个主机名交替出现就是这种情况。只保留一处负责规范主机名,建议放在 Cloudflare 规则里,源站只管返回内容。

为什么长期应该用 Full (strict)?

Flexible 只加密了浏览器到边缘这一段,边缘到源站仍是明文,访客的表单、Cookie 在这一段可以被沿途网络看到,而浏览器地址栏显示的是锁。Full 加密了第二段,但不校验证书,中间人用任意自签名证书都能冒充源站。Full (strict) 两段都加密,并确认对端确实是你的服务器。

另一个实际好处是排障简单:协议在两段上一致,源站看到的 scheme 就是访客的 scheme,前面第 3、4 条那类问题基本不会再出现。

怎么签发 Cloudflare Origin CA 证书并在 Nginx 配置?

Origin CA 证书免费,只被 Cloudflare 信任,专门用于边缘到源站这一段。步骤如下:

  1. 控制台进入 SSL/TLS → Origin Server,点 Create Certificate。
  2. 选择由 Cloudflare 生成私钥,密钥类型 RSA 或 ECC 均可;主机名填 example.com 和 *.example.com;有效期按需选择(最长可达 15 年,具体选项以控制台为准)。
  3. 生成后页面会显示证书和私钥。私钥只显示这一次,立即保存到源站:
sudo mkdir -p /etc/ssl/cloudflare
sudo nano /etc/ssl/cloudflare/example.com.pem   # 粘贴证书
sudo nano /etc/ssl/cloudflare/example.com.key   # 粘贴私钥
sudo chmod 600 /etc/ssl/cloudflare/example.com.key
  1. Nginx 配置:
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name example.com www.example.com;

    ssl_certificate     /etc/ssl/cloudflare/example.com.pem;
    ssl_certificate_key /etc/ssl/cloudflare/example.com.key;
    ssl_protocols       TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
    }
}

较旧版本的 Nginx 不支持单独的 http2 on;,改成 listen 443 ssl http2;。

  1. sudo nginx -t && sudo systemctl reload nginx,然后回控制台把模式改成 Full (strict)。
  2. 再跑一遍第一步的 curl:请求 https:// 地址应直接 200;请求 http:// 地址应只有一次 301 到 HTTPS,然后 200。
  3. 要从本机验证源站证书,从 Cloudflare 官方文档下载 Origin CA 根证书(RSA 或 ECC,与第 2 步的密钥类型对应),用它代替系统信任库:
curl -sI --cacert origin_ca_rsa_root.pem \
  --resolve example.com:443:203.0.113.10 https://example.com/

两点注意:Origin CA 证书不被浏览器信任,一旦把 DNS 记录切到灰色云朵(仅 DNS),访客会直接看到证书错误;如果还想限制只有 Cloudflare 能连源站,可以在此基础上开启 Authenticated Origin Pulls,或在防火墙只放行 Cloudflare 公布的 IP 段。回源保护的其他做法见 用 Cloudflare 加速 AI 应用:缓存、API 限速与回源保护怎么配。

修完之后的检查表

  • SSL/TLS 模式为 Full (strict)
  • curl -sIL https://example.com/ 没有跳转或最多一跳,最终 200
  • curl -sIL http://www.example.com/ 跳到规范地址后 200,没有来回
  • 直连源站 443 校验证书通过(公共 CA 证书直接去掉 -k;Origin CA 证书用 --cacert 指定根证书)
  • 改配置后清过 Cloudflare 缓存,浏览器用无痕窗口复测
  • 源站只有一处负责 HTTPS 跳转,只有一处负责主机名规范化

如果配置都对,访问仍然时好时坏,问题可能不在你这边。cdnping 的 API 状态页 每 15 分钟同步一次 Cloudflare 官方状态页,可以顺手看看当时有没有区域性事件;想比较各家公共 CDN 在国内的连接表现,可以看 CDN 速度榜 和 国内怎么选公共 CDN。更完整的本地网络排查顺序在 开发者网络自检清单(2026 版)。需要基于 Cloudflare 网络做边缘加速的团队,可以看 cdnping Edge 的介绍;中国大陆境内加速需要 ICP 备案,并经由持证服务商。

相关文章

继续阅读