Cloudflare Tunnel 配置实战:无需备案安全暴露内网服务
背景:公司测试环境需要外网访问,但阿里云国内服务器 80/443 端口必须 ICP 备案。本文记录如何通过 Cloudflare Tunnel 绕过备案限制,安全暴露内网服务。
一、问题背景
1.1 需求场景
- 开发测试环境部署在阿里云 ECS(杭州区域)
- 需要随时随地访问测试网站
- 应用运行在
localhost:5000
1.2 遇到的坑
| 问题 | 原因 |
|---|---|
| 开放 5000 端口给任意 IP | 安全风险极高,易被扫描攻击 |
| 使用 Nginx + 80 端口 | 阿里云国内服务器 ICP 备案拦截,返回 403 |
| 使用 Nginx + 8080 端口 | URL 带端口号,不美观 |
| 直接暴露公网 IP | 源站 IP 泄露,易被 DDoS |
1.3 核心诉求
- ✅ 无需 ICP 备案
- ✅ 使用标准 443 端口(HTTPS)
- ✅ 隐藏真实服务器 IP
- ✅ 自定义域名(如
blog.elliotz.men) - ✅ 免费或低成本
二、解决方案:Cloudflare Tunnel
2.1 什么是 Cloudflare Tunnel?
Cloudflare Tunnel(原 Argo Tunnel)是 Cloudflare 提供的内网穿透服务,核心特点:
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
│ 用户浏览器 │ → │ Cloudflare Edge │ → │ cloudflared │
│ (全球任意位置)│ │ (全球 CDN 节点) │ │ (本地守护进程) │
└─────────────┘ └─────────────────┘ └──────┬──────┘
│
┌────────▼────────┐
│ localhost:5000 │
│ (你的应用) │
└─────────────────┘
流量走向:
1. 用户访问 https://blog.elliotz.men
2. DNS 解析到 Cloudflare Edge(最近 CDN 节点)
3. Cloudflare Edge 通过 outbound 连接(非入站)找到本地 cloudflared
4. cloudflared 将请求转发到 localhost:5000
2.2 核心优势
| 优势 | 说明 |
|---|---|
| 无需开放入站端口 | 服务器防火墙可全部关闭,cloudflared 主动 outbound 连接 |
| 无需公网 IP | 甚至可以在 NAT/内网环境运行 |
| 自动 HTTPS | Cloudflare 边缘自动签发证书,无需手动配置 SSL |
| 隐藏源站 IP | 用户只能看到 Cloudflare IP,保护源站 |
| 无需 ICP 备案 | 流量通过 Cloudflare 国外节点,不受国内备案限制 |
| 免费 | Cloudflare Free 计划即可使用 |
2.3 与传统方案对比
| 方案 | 备案 | 暴露 IP | HTTPS | 成本 | 复杂度 |
|---|---|---|---|---|---|
| 直接开放端口 | ❌ | ✅ 暴露 | 手动配置 | 低 | 低 |
| Nginx + 80/443 | 必须 | ✅ 暴露 | 手动配置 | 低 | 中 |
| Nginx + 非标准端口 | ❌ | ✅ 暴露 | 手动配置 | 低 | 中 |
| Cloudflare Tunnel | ❌ 无需 | ❌ 隐藏 | 自动 | 免费 | 中 |
| FRP/ngrok | ❌ | 部分暴露 | 手动配置 | 中 | 中 |
三、配置实战
3.1 准备工作
- 注册 Cloudflare 账号(免费):https://dash.cloudflare.com
- 注册域名(本文使用
elliotz.men,Cloudflare 注册,5年140元) - 一台 Linux 服务器(本文使用 Ubuntu 24.04)
- 本地应用已运行(本文使用
localhost:5000)
3.2 安装 cloudflared
# 下载安装包
cd /tmp
curl -L --output cloudflared.deb \
https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
# 安装
sudo dpkg -i cloudflared.deb
# 验证安装
cloudflared --version
# 输出:cloudflared version 2026.3.0
3.3 创建 Tunnel
方式一:通过 Cloudflare Zero Trust Dashboard(推荐)
- 访问 https://one.dash.cloudflare.com
- Networks → Tunnels → Create a tunnel
- 选择 Cloudflared
- 填写名称:
elliot - 保存后会显示安装命令,复制 Token:
sudo cloudflared service install <TOKEN>
方式二:命令行创建(需要证书)
# 登录(浏览器认证)
cloudflared tunnel login
# 创建隧道
cloudflared tunnel create elliot
# 获取隧道 UUID
cloudflared tunnel list
3.4 配置路由
在 Dashboard 中配置 Public Hostname
- 进入 Tunnel 详情页 → Public Hostname 标签
- 点击 Add a public hostname
- 填写:
- Subdomain:
blog - Domain:
elliotz.men - Path: (留空)
- Type: HTTP
- URL:
localhost:5000 - 点击 Save hostname
或在配置文件中定义(备用方案)
# /etc/cloudflared/config.yml
tunnel: <TUNNEL-UUID>
credentials-file: /etc/cloudflared/<TUNNEL-UUID>.json
ingress:
- hostname: blog.elliotz.men
service: http://localhost:5000
- service: http_status:404
3.5 配置 DNS
在 Cloudflare Dashboard → DNS 中添加记录:
| Type | Name | Target | Proxy Status |
|---|---|---|---|
| CNAME | blog |
<TUNNEL-UUID>.cfargotunnel.com |
🟠 Proxied |
注意:Target 是隧道的 CNAME 地址,格式为
<UUID>.cfargotunnel.com
3.6 启动服务
# 启动并设为开机自启
sudo systemctl start cloudflared
sudo systemctl enable cloudflared
# 查看状态
sudo systemctl status cloudflared
# 查看日志
sudo journalctl -u cloudflared -f
3.7 验证访问
curl -I https://blog.elliotz.men
# 预期输出:
# HTTP/2 200
# server: cloudflare
# cf-cache-status: DYNAMIC
四、踩坑记录
4.1 错误:503 Service Unavailable
现象:
HTTP/2 503
cf-cache-status: DYNAMIC
原因:Tunnel 未配置 ingress 规则
解决:
- 检查 Dashboard 中是否添加了 Public Hostname
- 或检查 /etc/cloudflared/config.yml 是否有 ingress 配置
- 重启服务:sudo systemctl restart cloudflared
4.2 错误:403 Forbidden(阿里云备案拦截)
现象:
<title>Non-compliance ICP Filing</title>
原因:阿里云国内服务器 80/443 端口未备案被拦截
解决:使用 Cloudflare Tunnel(流量通过 Cloudflare 边缘,不走阿里云 80/443)
4.3 错误:No ingress rules were defined
日志:
WRN No ingress rules were defined... cloudflared will return 503
原因:Tunnel 运行了但没有配置路由规则
解决:在 Dashboard 的 Public Hostname 标签页添加路由,不是 Private Hostname
4.4 域名解析不生效
排查:
# 检查 DNS 解析
nslookup blog.elliotz.men
# 检查是否指向 Cloudflare
dig blog.elliotz.men +short
# 应返回 Cloudflare 的 CNAME 或 IP
解决:等待 DNS 传播(通常几分钟),或检查 DNS 记录的 Proxy 状态是否为橙色云
五、进阶配置
5.1 多服务路由
ingress:
- hostname: blog.elliotz.men
service: http://localhost:5000
- hostname: api.elliotz.men
service: http://localhost:3000
- hostname: admin.elliotz.men
service: http://localhost:8080
originRequest:
noTLSVerify: true
- service: http_status:404
5.2 添加基础认证(双重保护)
在 Nginx 层添加基础认证:
server {
listen 80;
server_name localhost;
location / {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://localhost:5000;
}
}
5.3 配合 Cloudflare Access(零信任)
如需更严格的访问控制:
1. Cloudflare Dashboard → Access → Applications
2. 添加 blog.elliotz.men
3. 配置身份提供商(Google、GitHub、OTP 等)
4. 设置访问策略(如仅允许公司邮箱)
六、成本分析
| 项目 | 费用 | 说明 |
|---|---|---|
| Cloudflare 账号 | 免费 | Free 计划即可 |
| Cloudflare Tunnel | 免费 | 不限流量 |
域名 elliotz.men |
140元/5年 | Cloudflare 注册,比国内便宜 |
| 阿里云 ECS | 按需 | 原有服务器,无额外费用 |
| 总计 | 140元/5年 | 约 28元/年 |
💡 对比:国内
.com域名通常 60-80元/年,Cloudflare 注册确实更便宜
七、总结
7.1 核心原理
Cloudflare Tunnel 的本质是反向 WebSocket 连接:
- 本地
cloudflared主动连接 Cloudflare Edge(outbound) - 建立持久 WebSocket 隧道
- 用户请求到达 Cloudflare Edge
- Edge 通过已有隧道将请求转发给本地
- 本地响应原路返回
关键:服务器无需开放任何入站端口,防火墙可以全部关闭。
7.2 适用场景
- ✅ 开发测试环境外网访问
- ✅ 家庭 NAS/内网服务暴露
- ✅ 绕过 ISP 封锁或备案限制
- ✅ 保护源站 IP 防止 DDoS
- ✅ 快速临时分享本地服务
7.3 不适用场景
- ❌ 高带宽视频流(有速度限制)
- ❌ 生产环境核心服务(建议配合 Access)
- ❌ 需要固定出口 IP 的场景
八、参考链接
作者:运维 🔧
日期:2026-05-12
标签:#cloudflare #tunnel #内网穿透 #网络安全 #免备案