为了救活晚高峰掉线的探针,我顺手把监控和博客都干趴了……

摘要:在解决国内局域网设备(玩客云 Armbian)跨境直连海外 VPS 晚高峰丢包 40% 的过程中,开启 Cloudflare CDN 代理(小黄云)本该是最直观的加速与抗丢包手段。然而,紧随其后的却是主站与个人博客双双陷入 ERR_TOO_MANY_REDIRECTS(301 死循环)、探针回源报 Error 525、甚至遭遇 Cloudflare Enterprise 专属付费拦截的一系列连环踩坑。本文记录了从盲目排查到抓包溯源,最终揭开 Nginx Proxy Manager 底层机制并 100% 免费破局的完整实录。


一、 背景与起因:跨洋之痛与“随手开启的小黄云”

在我的家庭网络环境中,有一台刷了 Armbian 的玩客云小主机,负责日常爬虫采集与数据推送。为了监控各端节点的存活与网络健康状况,我在一台海外 VPS 上搭建了 Komari Monitor 监控服务以及个人 WordPress 博客,并通过玩客云上的客户端(komari-agent)持续向服务端上报心跳指标。

但跨国网络环境向来充满变数:每逢晚高峰(20:00 – 23:00),国内骨干网出口拥堵,玩客云直连海外 VPS 的链路丢包率飙升至 35% ~ 50%。监控面板上的玩客云频繁在“在线”与“离线”之间反复横跳,日志里充斥着心跳连接超时和 WebSocket 异常断开。

此时,最顺理成章的优化方案摆在眼前——Cloudflare CDN(小黄云)。

凭借 Cloudflare 优质的 Anycast 全球边缘网络,流量从国内经优化路由进入 Cloudflare 边缘节点,再通过其跨洋内网骨干回源到 VPS,不仅能够将丢包率压到接近 0%,还能为博客和面板提供免费的 Web 安全防护与静态资源加速。

我在 Cloudflare DNS 控制台找到 komari 和 blog 的 A 记录,顺手将代理状态从灰色切换为醒目的橙色:【已代理(小黄云 ☁️)】。

点击保存的那一刻,我以为性能优化已经大功告成。然而,现实却给我上了一堂深刻的运维排障课。


二、 灵异事件:小黄云开启后的“连环崩塌”

刚开启小黄云不到一分钟,刷新浏览器测试,预想中的丝滑并没有到来,反而迎来了连环翻车现场:

现象 1:主站与博客双双陷入“死循环重定向”

在浏览器中打开 https://komari.example.com 或 https://blog.example.com,页面并没有正常展示,而是转圈数秒后直接弹出 Chrome 经典的报错:

ERR_TOO_MANY_REDIRECTS
该网页无法正常运作,komari.example.com 将您重定向的次数过多。

【 死循环重定向 (301 Loop) 产生链路 】

  访客浏览器                  Cloudflare CDN                   源站 VPS (NPM)
      │                             │                                │
  (1) │ 访问 https://komari          │                                │
      ├────────────────────────────>│                                │
      │                             │ (2) 回源转发 (HTTP)             │
      │                             ├───────────────────────────────>│
      │                             │                                │
      │                             │ (3) 触发 Force SSL, 强制返回 301 │
      │                             │<───────────────────────────────┤
      │                             │     (Location: https://komari) │
      │ (4) 将 301 抛回给浏览器       │                                │
      │<────────────────────────────┤                                │
      │                             │                                │
  (5) │ 遵循 301, 再次请求 https     │                                │
      ├────────────────────────────>│  (重复步骤 2~4,进入无休止死循环!) 

现象 2:探针单独走新域名,迎来大红叉 Error 525

为了不影响主站访问,我尝试另辟蹊径:在 DNS 中新建一个单独给探针使用的子域名 probe.example.com 并开启小黄云。结果更糟糕,访问该域名直接触发了 Cloudflare 拦截页:

Error 525: SSL handshake failed
Cloudflare is unable to establish an SSL connection to the origin server.


三、 屡试屡败的“常规解法”

面对这两个问题,我开启了第一轮排查,但几乎把网上能搜到的经验踩了个遍:

1. 经验主义失效:“SSL 模式从灵活切换到完全严格”?

网上 90% 关于“Cloudflare 重定向死循环”的文章都给出了标准答案:

“去 Cloudflare 后台将 SSL/TLS 加密模式从 Flexible 改为 Full 或 Full (Strict)。”

但我点开 Cloudflare 后台一看,域名的加密模式原本就一直设置在【完全(严格)/ Full (Strict)】!
按常理,Full (Strict) 下 Cloudflare 会使用 HTTPS (443) 并校验源站证书后回源,根本不应该存在因明文 HTTP 握手而引起的重定向。这个结论一度让我怀疑是 Cloudflare 节点的缓存延迟。

2. 规则救场?遭遇 Enterprise 权限暴击

既然 probe 域名回源报 525 握手失败,原因显然是源站 OpenResty 只有 komari.example.com 的证书,SNI 对不上导致握手终止。

🔒 Cloudflare 规则弹窗拦截:
“仅 Enterprise 套餐提供。升级计划来启用此功能。”
(注:Cloudflare 仅向月费数千美元的 Enterprise 客户开放 Origin Rules 中的重写主机标头功能)


四、 拨云见日:用抓包揭开真相

不再盲猜,直接使用终端命令行对真实链路进行“剥洋葱”式的逐层探测。

1. 抓取 301 响应头,确认重定向发起者

通过 curl -Iv 对重定向循环进行捕获:

curl -Iv https://komari.example.com/

终端打印出的响应头暴露了关键细节:

HTTP/2 301 
date: Sun, 18 Oct 2026 13:58:10 GMT
content-type: text/html
location: https://komari.example.com/
server: cloudflare
...
[HTML 页面内容]
301 Moved Permanently
openresty

注意这几个字段:

  1. HTML 响应体中的 openresty 标识:重定向页面根本不是 Cloudflare 边缘规则生成的,而是 VPS 上的源站 OpenResty 吐出来的!
  2. VPS 上的反向代理容器正是基于 OpenResty 开发的 Nginx Proxy Manager (NPM)。

2. 深入 NPM 机制:罪魁祸首竟是【Force SSL】!

我们进一步测试绕过 Cloudflare,直连 VPS 的 443 和 80 端口:

  • 直连 443 端口(带 Let’s Encrypt 证书):

    curl -s --resolve "komari.example.com:443:1.2.3.4" "https://komari.example.com"

    👉 返回 200 OK,页面完整加载!

  • 直连 80 端口:

    curl -I "http://1.2.3.4/" -H "Host: komari.example.com"

    👉 返回 301 Moved Permanently,内容与 Cloudflare 代理时完全一致!

真相终于大白:

在 Nginx Proxy Manager 控制台(http://vps-ip:81)中,我们在配置 komari 站点时随手勾选了 【Force SSL】(强制使用 SSL)。

在 NPM 底层,Force SSL 的本质是无脑植入了一段判断逻辑:

# Nginx Proxy Manager Force SSL 逻辑
if ($scheme = "http") {
    return 301 https://$host$request_uri;
}

当开启 Cloudflare 小黄云后,回源请求经过 CDN 时,即使浏览器端是 HTTPS,如果回源握手或反代未正确透传 $scheme,NPM 便会认定当前连接“不够安全”,忠实地抛出 301 -> https://komari.example.com。

浏览器收到后,再次带着 HTTPS 发起请求给 Cloudflare,Cloudflare 再次把请求递给 NPM,NPM 再次丢出 301……双方在门口互相推诿,形成了一个永无止境的死循环!

WordPress 博客(blog.example.com)的原理同理:WordPress 核心代码自带规范 URL 重定向,当收到不含 HTTPS=on 标识的代理请求时,WordPress 自身会强制做 301 重定向,与 NPM 和 CDN 叠加上演“死循环三重奏”。


五、 终极解决方案:三步破局(0 成本、0 报错)

弄清底层原理后,修复只需一分钟,且无需修改复杂的代码。核心设计哲学是 “外紧内松”:
外层由 Cloudflare 权威 Edge 证书阻挡所有不安全流量并提供 CDN 保护,内层源站 NPM 卸下防备,专注于纯粹的反代转发。

步骤 1:关闭 NPM 的【Force SSL】(破除 301 死循环)

  1. 登录 VPS 上的 Nginx Proxy Manager 后台(http://vps-ip:81)。
  2. 在 Proxy Hosts 列表找到 komari.example.com 和 blog.example.com,点击右侧的 Edit (编辑)。
  3. 切换到 【SSL】 选项卡。
  4. 取消勾选【Force SSL】(关闭强制跳转开关)。
  5. 点击 Save。

原理:取消后,NPM 不再针对进站请求强刷 301。无论 Cloudflare 回源以何种形式到达,NPM 都直接将请求交给后端容器,死循环瞬间瓦解!


步骤 2:多域名复用(解决探针 525 握手失败与企业版限制)

不需要购买 Enterprise 套餐重写标头,直接让源站 NPM 原生支持 probe 域名:

  1. 在刚刚的 NPM 编辑弹窗中,切换到 【Details】 选项卡。
  2. 在 Domain Names 文本框中,键入 probe.example.com 并按下 Enter 回车键。
  3. 确保框内同时存在:
    • komari.example.com
    • probe.example.com
  4. 勾选 Websockets Support(监控探针与网页终端心跳通信必须)。
  5. 点击 Save。

原理:NPM 自动将 probe 与 komari 指向同一个后端。此时 Cloudflare 带 probe.example.com 回源时,NPM 能正确响应,525 握手错误彻底消除。


步骤 3:WordPress 适配 Cloudflare 代理标头(防止博客偶发重定向)

针对个人博客,在 VPS 的 WordPress 根目录下编辑 wp-config.php,在 /* That's all, stop editing! Happy publishing. */ 一行上方加入以下代码:

// 信任 Cloudflare 反向代理的 HTTPS 标头,防止 301 循环
if (isset($_SERVER['HTTP_CF_VISITOR']) && strpos($_SERVER['HTTP_CF_VISITOR'], 'https') !== false) {
    $_SERVER['HTTPS'] = 'on';
}
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

保存后,WordPress 无论在何种 CDN 架构下都能精准感知当前的加密通道,彻底告别重定向混乱。


额外神技:Cloudflare Worker 免费白嫖多路反代

如果在某些特殊场景下无法修改源站配置,我们还可以利用 Cloudflare 每天免费 10 万次调用的 Cloudflare Workers 来免费平替 Enterprise 的标头重写功能:

export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    // 探针专属加速路由:重写 Host 并转发
    if (url.hostname.includes('probe')) {
      url.hostname = 'komari.example.com';
      const newReq = new Request(url, request);
      newReq.headers.set('Host', 'komari.example.com');
      return fetch(newReq);
    }

    // 默认路由
    return fetch(request);
  }
};

在 Worker 绑定自定义域后,等同于获得了企业版的 Host Header Rewrite 能力,全程 0 费用。


六、 成果验证

配置生效后,在玩客云小主机上将探针服务地址切换为 probe.example.com:

sed -i 's|komari.example.com|probe.example.com|g' /etc/systemd/system/komari-agent.service
systemctl daemon-reload && systemctl restart komari-agent

再在终端进行全链路检验:

  1. 访问测试:curl -I https://komari.example.com 直接返回 HTTP/2 200 OK,博客与监控后台秒开。
  2. 探针状态:玩客云上 systemctl status komari-agent 显示 WebSocket 握手成功,心跳平稳。
  3. 丢包对比:晚高峰期间通过 Cloudflare 边缘路由,持续 ping 测 1000 个数据包,丢包率从直连的 40% 骤降至 0%,延迟方差显著收窄,探针再也没有闪断过。

七、 总结与避坑心得

这次排障经历虽然曲折,但提炼出了几条极为实用的运维铁律:

  1. 别被表面现象带偏,善用抓包定位责任人:遇到 301 重定向循环时,第一步不是去盲改 CDN 设置,而是运行 curl -Iv 查看响应头里的 Server 和 HTML 内容。到底是谁抛出的 301,一目了然。
  2. CDN 体系下的“双层 SSL 悖论”:在“CDN 边缘 + 源站反代”双层架构中,源站切忌开启强硬的 Force SSL 或强制跳转规则。外部 CDN 负责接管用户侧的 HTTPS 握手与证书升级,源站反代只需无条件透传。
  3. 遇到付费墙,多想想轻量级替代:Cloudflare 的高级规则虽好,但往往有商业化限制。善用其免费提供的 Workers、Page Rules 以及源站反代本身的多域名别名机制,绝大多数需求都能零成本优雅解决。
上一篇 为多个 Docker Compose 项目编写自动更新脚本:打造轻量、可控的无人值守运维流
目录
keiomn

keiomn管理员

这个人很懒,什么都没有留下。

本月创作热力图

少
多