记一次 Cloudflare + frp + OpenResty 网站无法访问的完整排查过程
本文记录了一次从 "Cloudflare 525 错误" 到最终定位根因并修复的完整排查过程,涉及 Cloudflare、frp 内网穿透、OpenResty(Docker 部署)的联动问题,希望对有类似架构的同学有所帮助。
一、架构背景
在介绍问题之前,先说明一下整体架构:
用户浏览器
↓ HTTPS
Cloudflare CDN(域名托管于 CF,开启代理)
↓ HTTPS (Full 模式)
公网云服务器(运行 frps)
↓ frp TCP 隧道
本地内网服务器(运行 frpc + OpenResty Docker 容器)
↓ 反向代理
本地应用服务(某端口)
- 本地服务器:家用/自建物理服务器,无公网 IP,通过 frp 内网穿透暴露服务
- 公网服务器:云服务器,运行 frps,作为中转节点
- OpenResty:以 Docker 容器方式运行在本地服务器,监听 80/443 端口
- frpc:以 Docker 容器方式运行在本地服务器,host 网络模式,将本地 80/443 穿透到公网服务器对应端口
- Cloudflare:SSL 模式为 Full,域名解析到公网服务器 IP
二、问题现象
访问网站时,浏览器显示 Cloudflare 错误页面,提示:
Your Browser Working
Cloudflare Working
Host Error
初步判断是源站(本地服务器)出现了问题。
三、排查过程
第一步:确认基础组件状态
首先在本地服务器上确认端口监听情况:
ss -tlnp | grep -E ':80|:443'
输出显示 OpenResty 正在监听 80 和 443 端口,进程存在,看起来服务是正常的。
ps aux | grep -E 'nginx|openresty'
进程也在,无明显异常。
第二步:查看 frpc 日志
查看 frpc 容器日志,发现大量报错:
[web-reverse-proxy2] connect to local service [x.x.x.x:443] error:
dial tcp x.x.x.x:443: connect: connection refused
[web-reverse-proxy1] connect to local service [x.x.x.x:80] error:
dial tcp x.x.x.x:80: connect: connection refused
connection refused 说明:frp 隧道本身是通的,但 frpc 去连接本地的 80/443 时被拒绝了。
第三步:查看 OpenResty 日志
查看 OpenResty 容器日志,发现有请求进来,但全部返回 403,且响应体大小达到 16KB+(说明这是 WAF/默认 server 的拦截页,而非正常业务响应)。这说明在某段时间内,OpenResty 的 lyc1466.cn 站点配置没有生效,请求落到了默认 server。
后来发现这是因为 OpenResty 曾经短暂宕机,之后被重新拉起后,1Panel 触发了两次配置 reload(SIGHUP),配置重新生效。
第四步:模拟本地访问验证
在本地服务器上,分别用回环地址和内网 IP 测试:
# 用回环地址测试
curl -Ik https://127.0.0.1 -H "Host: yourdomain.com"
# 结果:HTTP/1.1 200 OK ✅
# 用内网 IP 测试
curl -Ik https://192.168.x.x -H "Host: yourdomain.com"
# 结果:curl: (7) Failed connect to 192.168.x.x:443; 拒绝连接 ❌
关键差异浮现:OpenResty 响应 127.0.0.1,但不响应内网 IP!
而 frpc 配置中,localIP 写的是内网 IP,导致 frpc 一直连接失败:
# frpc 配置(问题所在)
[[proxies]]
name = "web-reverse-proxy2"
type = "tcp"
localIP = "192.168.x.x" # ← 用的是内网 IP
localPort = 443
remotePort = 443
第五步:公网服务器侧验证
在公网服务器上用 openssl s_client 直接测试 SSL 握手:
openssl s_client -connect <公网IP>:443 -servername yourdomain.com 2>&1 | head -20
输出结果:
CONNECTED(00000003)
SSL handshake has read 0 bytes and written 308 bytes ← 致命
no peer certificate available
Cipher is (NONE)
- TCP 连接建立成功(
CONNECTED) - 客户端发出了 ClientHello(written 308 bytes)
- 服务端完全没有回应(read 0 bytes)
- 无证书返回
这与 frpc 无法连接本地 443 的问题完全对应:frps 把流量转发给 frpc,frpc 连不上 OpenResty,frps 只能关闭连接,Cloudflare 收到握手失败,报告 525 错误。
四、根本原因
OpenResty Docker 容器(host 网络模式)在某次 reload 后,实际只响应 127.0.0.1 的连接,不响应内网 IP 的连接。
在 Linux 网络中,127.0.0.1 和内网 IP(如 192.168.x.x)虽然是同一台机器,但走的是不同的网络接口:
| 访问地址 | 网络接口 | 是否受 firewalld 限制 |
|---|---|---|
127.0.0.1 | lo(回环) | ❌ 不经过,完全绕过 |
192.168.x.x | eth0 | ✅ 经过 firewalld/iptables 完整规则链 |
frpc 用内网 IP 去连接 OpenResty,但 OpenResty 在该接口上的连接被拒绝,导致整条链路断裂。
五、修复方案
修改 frpc 配置,将所有本机服务的 localIP 从内网 IP 改为 127.0.0.1:
# 修复前
[[proxies]]
name = "web-reverse-proxy1"
type = "tcp"
localIP = "192.168.x.x" # ❌
localPort = 80
remotePort = 80
[[proxies]]
name = "web-reverse-proxy2"
type = "tcp"
localIP = "192.168.x.x" # ❌
localPort = 443
remotePort = 443
# 修复后
[[proxies]]
name = "web-reverse-proxy1"
type = "tcp"
localIP = "127.0.0.1" # ✅
localPort = 80
remotePort = 80
[[proxies]]
name = "web-reverse-proxy2"
type = "tcp"
localIP = "127.0.0.1" # ✅
localPort = 443
remotePort = 443
重启 frpc:
docker restart frpc
验证修复:
curl -I https://yourdomain.com
# HTTP/2 200 ✅
六、完整的错误链路 vs 修复后链路
修复前(故障链路)
Cloudflare
↓ HTTPS
frps:443(公网服务器)
↓ 转发
frpc → 连接 192.168.x.x:443 → ❌ connection refused
↓
frps 无数据返回
↓
Cloudflare 收到握手失败 → 报 525 Error
修复后(正常链路)
Cloudflare
↓ HTTPS
frps:443(公网服务器)
↓ 转发
frpc → 连接 127.0.0.1:443 → ✅ 连接成功
↓
OpenResty(Docker, host 网络)
↓ 反向代理
本地应用服务
↓
HTTP 200 原路返回
七、经验总结
1. frpc 中本机服务应优先使用 127.0.0.1
# ✅ 推荐:本机服务用回环地址,绕过防火墙,更稳定
localIP = "127.0.0.1"
# ⚠️ 仅在目标是其他局域网设备时才用局域网 IP
localIP = "192.168.x.x" # 目标是局域网内其他机器
2. Cloudflare 525 排查思路
遇到 525 错误,按以下顺序排查:
1. 先检查源站 443 端口是否在监听
ss -tlnp | grep :443
2. 用 openssl 测试 SSL 握手
openssl s_client -connect <源站IP>:443 -servername <域名>
3. 检查证书有效期
openssl x509 -in /path/to/fullchain.pem -noout -enddate
4. 检查 Cloudflare SSL 模式(不能是 Flexible)
5. 分别用 127.0.0.1 和内网 IP 测试,排查防火墙问题
3. 遇到 connection refused 和 connection timeout 的区别
| 错误类型 | 含义 | 排查方向 |
|---|---|---|
connection refused | 端口有监听但主动拒绝,或根本无进程监听 | 检查服务是否运行、防火墙规则 |
connection timeout | 包被丢弃,无响应 | 检查防火墙 DROP 规则、安全组 |
no route to host | 路由不可达 | 检查网络配置、路由表 |
4. Docker host 网络模式下的注意事项
Docker 容器使用 host 网络模式时,容器与宿主机共享网络栈,但以下情况需要注意:
- 容器内的服务如果只绑定了
127.0.0.1,从其他网卡(如eth0)发来的请求无法访问 - 其他容器(bridge 模式)访问宿主机服务时,需要用 Docker 网关 IP(通常是
172.17.0.1),而不是127.0.0.1
八、附:frp 内网穿透最佳实践配置参考
# frpc.toml 配置建议
# 本机服务 → 用 127.0.0.1
[[proxies]]
name = "local-web-http"
type = "tcp"
localIP = "127.0.0.1"
localPort = 80
remotePort = 80
[[proxies]]
name = "local-web-https"
type = "tcp"
localIP = "127.0.0.1"
localPort = 443
remotePort = 443
# 其他局域网设备 → 用对应设备的局域网 IP
[[proxies]]
name = "nas-http"
type = "tcp"
localIP = "192.168.x.x" # NAS 的局域网 IP
localPort = 80
remotePort = 48080
写在最后: 这次问题的本质是对 Linux 网络接口的理解不够深入——
127.0.0.1和192.168.x.x虽然都是本机,但走的网络路径完全不同,防火墙的处理结果也不同。在 Docker + frp + 防火墙的组合场景下,这类细节很容易被忽略,希望本文能帮助遇到类似问题的同学少走弯路。