Cloudflare + 反向代理回源踩坑三连:308 循环 / 522 黑洞 / 429 限流

·66 次浏览
  • 作者
    帖子
  • devops@devops
    楼主

    背景

    给多个站点接入 Cloudflare 加速,架构是 CF → 边缘反代服务器 (nginx) → 源站。过程中连续踩了三个坑,都跟 CF 的回源机制有关,记录下来方便排查。

    坑一:CF Flexible + 源站 308 = 重定向循环

    现象:站点开启 CF Proxied 后浏览器报 「ERR_TOO_MANY_REDIRECTS」 。

    原因:CF SSL 设置为 Flexible 模式时,CF 用 HTTP 80 回源。但源站 nginx 对所有 HTTP 请求做了 308 跳 HTTPS:

    if ($scheme = http) {
        return 308 https://$host$request_uri;
    }
    

    CF 收到 308 后跟随跳转,再次用 HTTP 连源站 → 又收到 308 → 死循环。

    修复:让源站识别 CF 流量,跳过重定向。 CF 回源时会带 X-Forwarded-Proto header,利用这个做条件判断:

    # 变量组合:只对 「HTTP 且非 CF 」的请求做 308
    set $do_redirect "";
    if ($scheme = http) {
        set $do_redirect "H";
    }
    if ($http_x_forwarded_proto = "") {
        set $do_redirect "${do_redirect}N";
    }
    if ($do_redirect = "HN") {
        return 308 https://$host$request_uri;
    }
    

    逻辑:$scheme=http(H)+ 没有 X-Forwarded-Proto(N)= 直接访问才跳转。 CF 回源带了这个 header,不会触发 308 。

    nginx 知识点:nginx 的 if 不支持 AND/OR 逻辑运算符,需要用 set 变量拼接字符串来模拟组合条件,这是 nginx 社区常用的 pattern 。

    备选方案:在源站安装 CF Origin Certificate,SSL 模式改为 Full,CF 直接用 HTTPS 回源。但需要管理证书,不如改 nginx 条件简单。

    坑二:CF for SaaS fallback origin 指向黑洞 IP → 522

    现象:域名 DNS 已指向 CF,Proxied 已开,但访问返回 522(Connection timed out) 。

    原因:使用 CF for SaaS(SSL for SaaS) 时,fallback origin 的 A 记录指向了 192.0.2.1 。这是 RFC 5737 定义的文档保留地址(TEST-NET-1),不会路由到任何真实服务器,等同于黑洞。 CF 尝试回源到这个 IP,自然超时。

    修复:把 fallback origin 的 A 记录改为源站的真实 IP 。

    知识点:RFC 5737 定义了三个文档专用地址段:192.0.2.0/24(TEST-NET-1) 、 198.51.100.0/24(TEST-NET-2) 、 203.0.113.0/24(TEST-NET-3) 。如果在 DNS 记录里看到这些 IP,基本可以确定是占位符没改。

    坑三:反代服务器没传真实 IP → 后端 429 限流

    现象:站点通过 CF 访问时,部分静态资源 (CSS/JS) 返回 429 Too Many Requests 。直接访问源站正常。

    原因:CF → 反代服务器这段,反代的 nginx 配置了 set_real_ip_from + real_ip_header CF-Connecting-IP,看起来没问题。但 Ubuntu/Debian 默认安装的 nginx 没有编译 ngx_http_realip_module 模块,这些指令静默失效。

    结果:$remote_addr 仍然是 CF 边缘节点的 IP,传给后端的 X-Real-IP 也是 CF 的 IP 。后端应用 (这里是 Discourse) 把所有用户当成同一个 IP,触发 rate limit 。

    验证模块是否存在:

    nginx -V 2>&1 | grep -o 'real_ip'
    # 有输出说明模块已编译,无输出说明缺失
    

    修复:不依赖 realip 模块,直接用 CF 的 header 变量:

    proxy_set_header X-Real-IP $http_cf_connecting_ip;
    proxy_set_header X-Forwarded-For $http_cf_connecting_ip;
    

    $http_cf_connecting_ip 是 nginx 自动从请求头 CF-Connecting-IP 提取的变量,不需要任何额外模块。

    安全提示:如果反代服务器不是只接受 CF 流量,CF-Connecting-IP header 可以被客户端伪造。建议配合防火墙只允许 CF IP 段 访问反代的 80/443 端口。

    总结排查清单

    接入 CF 后出问题,按这个顺序排查:

    症状 检查项
    重定向循环 源站是否无条件 HTTP→HTTPS 跳转?CF SSL 模式是否 Flexible?
    522 超时 DNS A 记录是否指向真实 IP?是否有 SaaS fallback origin 没配?
    429 限流 反代是否正确传递了真实用户 IP?realip 模块是否存在?
    520 异常 源站是否返回了 CF 无法解析的响应?检查源站 error log
    525 SSL 握手失败 SSL 模式 Full 但源站没有有效证书?改 Flexible 或装 Origin Cert

Cyberforums · 原文 · Markdown

想回复或继续讨论?请到 Cyberforums 赛博论坛参与。

参与讨论 / 回复