从零搭建内网穿透并配置 HTTPS 证书:解决 Cloudflare 526 错误全记录

前言

很多开发者和个人站长会遇到这样的场景:服务跑在内网(家庭服务器、树莓派、虚拟机等),没有公网 IP,又想让外网用户通过自己的域名安全访问。内网穿透工具 frp 可以轻松解决网络可达性问题,而 Let’s Encrypt 通配符证书 配合 Nginx 能实现全站 HTTPS。然而,当你把穿透服务与 Cloudflare CDN 结合使用时,一不小心就会踩进 526 错误 的坑。本文将完整记录从搭建 frp 内网穿透,到申请通配符证书,再到修复 526 错误的详细过程,希望能帮助你顺利跑通全链路。


一、整体架构预览

先看一下我们的最终目标架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
用户浏览器


Cloudflare (CDN/DNS) ← SSL/TLS 模式: Full (strict)


公网服务器 (Nginx) ← 监听 443,使用 *.example.com 通配符证书


frps (公网 frp 服务端) ← 只提供 HTTP (8080 端口)

▼ (frp 隧道)
内网服务器 (frpc + 各服务) ← 本地端口 8456/5244/7451 等

核心逻辑

  • 所有流量经 Cloudflare 代理,Cloudflare 与源站之间必须走 HTTPS(Full strict 模式)。
  • 公网 Nginx 作为统一 HTTPS 入口,使用通配符证书解密流量。
  • 解密后的请求转发给 frps 的 HTTP 端口,frps 根据域名分发到内网对应服务。
  • 内网服务只提供 HTTP,无需关心 SSL,证书管理集中在公网 Nginx 上。

二、前期准备

1. 资源清单

  • 域名example.com(已在 Cloudflare 管理 DNS)
  • 公网服务器:一台有公网 IP 的 Linux(如 Ubuntu 22.04),IP 为 120.79.200.141
  • 内网服务器:运行实际服务的机器,可访问公网服务器
  • 内网服务端口举例
    • book.example.com127.0.0.1:8456
    • blog.example.com127.0.0.1:7451
    • openlist.example.com127.0.0.1:5244
    • beautyhub.example.com127.0.0.1:8453

2. Cloudflare 基础设置

  • 在 Cloudflare DNS 控制台中,为所有子域名添加 A 记录,指向公网服务器 IP。
  • 初期可先将代理状态设为 橙色云朵(Proxied),后续按需调整。

三、搭建内网穿透(frp)

1. 安装 frp

在公网服务器和内网服务器上都下载 frp 最新版(本文以 v0.61.1 为例):

1
2
3
wget https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_linux_amd64.tar.gz
tar -xzf frp_0.61.1_linux_amd64.tar.gz
cd frp_0.61.1_linux_amd64

公网服务器只需要 frpsfrps.toml;内网服务器只需要 frpcfrpc.ini(本文使用 TOML 格式,但 INI 也兼容)。

2. 配置 frps(公网服务端)

编辑 /root/frp/frps.toml

1
2
3
4
bindPort = 7000                    # frp 控制连接端口
auth.token = "your_strong_token" # 认证令牌,frpc 必须匹配

vhostHTTPPort = 8080 # 为所有 HTTP 虚拟主机提供的入口端口

这里我们只让 frps 提供 HTTP 转发能力,不碰 HTTPS。启动 frps:

1
./frps -c frps.toml

建议使用 systemd 管理自启。

auth.token = “your_strong_token”
这个是确保frps和frpc的口令一致,可自行设置密码,避免其他人盗用。

3. 配置 frpc(内网客户端)

编辑内网的 frpc.ini(或 frpc.toml):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
serverAddr = "120.79.200.141"
serverPort = 7000
auth.token = "your_strong_token"

[[proxies]]
name = "book"
type = "http"
localIP = "127.0.0.1"
localPort = 8456
customDomains = ["book.example.com"]

[[proxies]]
name = "blog"
type = "http"
localIP = "127.0.0.1"
localPort = 7451
customDomains = ["blog.example.com"]

# ... 其他子域名类似配置

所有代理类型为 http,内网服务只需提供 HTTP。启动 frpc:

1
./frpc -c frpc.ini

此时,如果你在公网服务器上访问 http://127.0.0.1:8080 并指定 Host 头为 book.example.com,应该能得到内网 8456 端口的响应。说明穿透隧道已通。


四、Nginx 作为 HTTPS 统一入口

我们希望在公网服务器上使用 Nginx 接收 443 端口的 HTTPS 请求,解密后转发给本地的 frps:8080

1. 安装 Nginx 并创建配置

1
sudo apt update && sudo apt install nginx

/etc/nginx/sites-available/ 下新建 frp-proxy 配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# HTTP 转 HTTPS 跳转
server {
listen 80;
server_name *.example.com example.com;
return 301 https://$host$request_uri;
}

# HTTPS 核心服务
server {
listen 443 ssl http2;
server_name *.example.com;

# 证书路径(稍后申请并填入)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

启用站点并重载 Nginx:

1
2
3
sudo ln -s /etc/nginx/sites-available/frp-proxy /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

此时若直接访问 HTTPS,会因为证书无效而报错,这正是我们需要申请的。


五、申请 Let’s Encrypt 通配符证书

通配符证书 *.example.com 可以保护所有一级子域名,免去逐个申请的麻烦。由于我们的域名 DNS 在 Cloudflare,将使用 Certbot 的 Cloudflare DNS 插件完成自动化验证与续期。

1. 安装 Certbot 及 Cloudflare 插件

1
sudo apt install certbot python3-certbot-dns-cloudflare

2. 获取 Cloudflare API Token

登录 Cloudflare → My Profile → API Tokens,创建一个权限为:

  • Zone:DNS:Edit
  • Zone:Zone:Read
    并限定到具体域名 example.com。生成 Token 后保存好。

3. 创建凭据文件

1
2
3
4
sudo tee /etc/letsencrypt/cloudflare.ini <<'EOF'
dns_cloudflare_api_token = "你的_API_Token"
EOF
sudo chmod 600 /etc/letsencrypt/cloudflare.ini

4. 申请通配符证书

1
2
3
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d "*.example.com" -d example.com

首次申请时,插件会自动在 DNS 中添加 TXT 记录并验证,成功后证书会保存在 /etc/letsencrypt/live/example.com/

5. 设置自动续期与 Nginx 重载

Certbot 自动续期需要添加 renew hook,编辑续期配置文件 /etc/letsencrypt/renewal/example.com.conf,在 [renewalparams] 段落下加入:

1
renew_hook = systemctl reload nginx

之后 Certbot 会在证书到期前 30 天自动执行续期并重载 Nginx。


六、Cloudflare SSL/TLS 模式设置

进入 Cloudflare 仪表盘 → 你的域名 → SSL/TLSOverview

  • 将加密模式设置为 **Full (strict)**。

该模式要求源站必须提供由公共信任 CA 签发的有效证书,我们的 Nginx 已经配置了 Let’s Encrypt 的通配符证书,完全满足要求。


七、踩坑与修复:526 错误的彻底解决

一切配置完成后,访问 book.example.com 可能仍然看到 Cloudflare 的 526 错误Invalid SSL certificate

1. 错误原因分析

  • Full (strict) 模式下,Cloudflare 会以 book.example.com 作为 SNI 连接源站 443 端口,并验证返回的证书。
  • Nginx 收到 SNI 后,根据 server_name 选择匹配的 server 块。如果存在多个 443 server 块,且某个块精准匹配了 book.example.com 但使用了无效证书,则会被优先匹配,导致错误。
  • 在我们环境中,残留了之前用 Certbot 单独为 beautyhub.example.com 申请的证书配置,里面包含了 book.example.com,但证书仅针对 beautyhub.example.com,因此不被信任。

2. 排查过程

首先找到所有监听 443 的配置:

1
grep -rn "listen.*443.*ssl" /etc/nginx/

输出可能包含多个文件,例如:

1
2
/etc/nginx/sites-available/default:12:    listen 443 ssl;
/etc/nginx/sites-available/frp-proxy:8: listen 443 ssl http2;

检查 default 文件内容,发现其使用了 beautyhub.example.com 的单域名证书,并且 server_name 包含了 book.example.com

3. 解决方案

删除冲突的旧配置,只保留通配符配置:

1
2
3
4
sudo mv /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
sudo rm /etc/nginx/sites-enabled/default # 如果有符号链接
sudo nginx -t
sudo systemctl reload nginx

4. 验证修复

在服务器本地模拟 Cloudflare 的 SNI 请求:

1
2
echo | openssl s_client -connect 127.0.0.1:443 -servername book.example.com 2>/dev/null \
| openssl x509 -noout -subject -ext subjectAltName

看到 subject=CN=*.example.comDNS:*.example.com,说明证书正确返回。访问域名,526 消失。


八、自动化与维护建议

  • 证书续期:已验证 certbot renew --dry-run 模拟成功,实际上每 12 小时会运行一次检查,到期前自动续期。
  • 新增子域名:只需在内网 frpc 配置中添加新的 [[proxies]] 段,并在 Cloudflare DNS 添加 A 记录(可选择是否开启代理),无需修改 Nginx 或证书。
  • 安全性
    • 限制 frps 的 auth.token 强度。
    • Cloudflare API Token 仅限必要的 DNS 编辑权限。
    • 公网服务器防火墙仅开放 80/443 和必要的 frp 端口。

九、总结

通过 frp + Nginx + Let’s Encrypt 通配符证书 + Cloudflare 的组合,我们构建了一个灵活、安全且易于扩展的内网穿透架构。核心原则是:

  • frp 只做 HTTP 转发,不碰 TLS,降低复杂度。
  • Nginx 统一卸载 SSL,使用通配符证书,所有子域名受益。
  • Cloudflare Full (strict) 确保全程加密,证书自动化续期免除维护烦恼。

希望这篇详细的踩坑记录能帮助你在自己的项目中顺利实现内网穿透与 HTTPS 全链路加密。如果你也遇到了 526 错误,不妨检查一下 Nginx 中是否有多余的 443 server 块在作祟。


十、未来新增子域名穿透

以后新增子域名,只需两个地方操作,Nginx 和证书完全不用动。


1. 内网 frpc 配置文件新增代理段

假设新子域名为 new.risespark.cn,内网服务运行在本地的 9000 端口。

在内网服务器的 frpc.ini 中添加:

1
2
3
4
5
6
[[proxies]]
name = "new"
type = "http"
localIP = "127.0.0.1"
localPort = 9000
customDomains = ["new.risespark.cn"]

保存后重启 frpc:

1
sudo systemctl restart frpc

2. Cloudflare DNS 添加记录

登录 Cloudflare → risespark.cn → DNS → 添加记录:

  • 类型A
  • 名称new
  • IPv4 地址120.79.200.141(你的公网服务器 IP)
  • 代理状态
    • 橙色云朵:走 Cloudflare CDN/HTTPS,隐藏源站
    • 灰色云朵:直连源站,不经过 Cloudflare

保存后稍等片刻(通常一分钟内生效)。


完成

访问 https://new.risespark.cn,即可正常访问内网服务。

不需要碰公网 Nginx、frps 和 SSL 证书,因为它们都基于通配符 *.risespark.cn,自动覆盖所有新增子域名。