公众号旧文
Cloudflare Tunnel + Nginx 多站点排障记录
我有一台位于洛杉矶的 VPS:
CPU:1 Core
内存:512MB
硬盘:1.2GB
系统:Debian 13
原本部署了两个网站:
lax-mini.19900429.xyz
cover.19900429.xyz
对应关系如下:
Cloudflare Tunnel
├── lax-mini.19900429.xyz -> localhost:80
└── cover.19900429.xyz -> localhost:8088
Nginx:
80 -> lax-mini
8088 -> YouTube Cover
虽然能用,但这种一站一端口的方式不够规范。
于是决定改造成:
Nginx
└── 80
├── lax-mini.19900429.xyz
└── cover.19900429.xyz
通过 server_name 区分站点。
第一步:修改 Nginx
默认站点:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name lax-mini.19900429.xyz;
root /var/www/html;
}
YouTube Cover:
server {
listen 80;
server_name cover.19900429.xyz;
root /var/www/youtube-cover;
}
重载:
nginx -t
systemctl reload nginx
本地验证:
curl -H "Host: lax-mini.19900429.xyz" http://127.0.0.1
返回:
<title>lax-mini</title>
再测试:
curl -H "Host: cover.19900429.xyz" http://127.0.0.1
返回:
<title>YouTube封面提取工具</title>
说明 Nginx 配置正确。
第二步:修改 Cloudflare Tunnel
原配置:
ingress:
- hostname: lax-mini.19900429.xyz
service: http://localhost:80
- hostname: cover.19900429.xyz
service: http://localhost:8088
改成:
ingress:
- hostname: lax-mini.19900429.xyz
service: http://localhost:80
- hostname: cover.19900429.xyz
service: http://localhost:80
重启:
systemctl restart cloudflared
第三步:发现问题
此时本地测试正常:
curl -H "Host: cover.19900429.xyz" http://127.0.0.1
返回:
<title>YouTube封面提取工具</title>
但外网访问:
curl https://cover.19900429.xyz
返回:
<title>lax-mini</title>
两个域名都显示同一个页面。
第四步:排查 Tunnel 配置
发现系统里居然存在两份配置:
/etc/cloudflared/config.yml
/root/.cloudflared/config.yml
查看:
cloudflared tunnel ingress rule https://cover.19900429.xyz
发现读取的是:
/root/.cloudflared/config.yml
而里面仍然保留:
cover -> localhost:8088
一度以为问题出在这里。
后来检查 systemd:
systemctl cat cloudflared
发现:
ExecStart=/usr/bin/cloudflared \
--config /etc/cloudflared/config.yml \
tunnel run
说明服务实际使用的是:
/etc/cloudflared/config.yml
这条线索最终被排除。
第五步:查看 Nginx 实际收到的 Host
修改日志格式:
log_format hostlog '$remote_addr "$host" "$http_host" "$request"';
访问网站后看到:
::1 "cover.19900429.xyz" "cover.19900429.xyz" "GET / HTTP/1.1"
说明:
Cloudflare Tunnel
↓
Nginx
Host Header 完整保留,Tunnel 没有问题。
第六步:真正的原因
查看两个站点配置,默认站点:
listen 80 default_server;
listen [::]:80 default_server;
Cover 站点:
listen 80;
缺少:
listen [::]:80;
而访问日志显示:
::1
说明 cloudflared 使用 IPv6 localhost 连接 Nginx。
于是出现:
IPv4
cover -> youtube-cover
IPv6
cover -> default_server
最终所有请求都落到了默认站点。
修复
改为:
server {
listen 80;
listen [::]:80;
server_name cover.19900429.xyz;
root /var/www/youtube-cover;
}
重载:
nginx -t
systemctl reload nginx
再次访问:
https://cover.19900429.xyz
恢复正常。
最终架构
Internet
│
▼
Cloudflare
│
▼
Cloudflare Tunnel
│
▼
localhost:80
│
▼
Nginx
├── lax-mini.19900429.xyz
│ └── /var/www/html
│
└── cover.19900429.xyz
└── /var/www/youtube-cover
所有网站统一监听 80 端口,通过 server_name 区分。
以后新增站点,只需要新增一个 Nginx Server Block 即可,无需再占用额外端口。
经验总结
- 一个服务器多个网站,优先使用
server_name,不要一站一端口。 - Cloudflare Tunnel 调试时要确认实际使用的是哪份配置文件。
- 默认读取的是用户目录配置,不一定是 systemd 服务使用的配置。
- 查看 Host Header 是排查反向代理问题最有效的方法之一。
- Nginx 同时部署 IPv4 和 IPv6 时,要成对出现,否则可能出现问题。