在传统的 VPS 运维场景中,我们要把内部服务暴露到公网,通常需要开放端口、配置防火墙规则、申请 SSL 证书、设置反向代理——这一套流程不仅繁琐,而且每多开一个端口就多一份被攻击的风险。如果你家里有 NAS、树莓派,或者公司内网有开发环境需要远程访问,但又不希望把服务直接挂到公网上,Cloudflare Tunnel 就是目前最优雅的解决方案之一。
本文将从零开始,在 VPS 和内网机器上部署 Cloudflare Tunnel,实现 HTTPS 自动配置、多服务路由、访问策略控制和故障恢复,帮助你构建一套生产可用的零信任访问架构。
什么是 Cloudflare Tunnel
Cloudflare Tunnel(原名 Argo Tunnel)是 Cloudflare 提供的一种反向隧道技术。它的核心思路是:在你的服务器上运行一个轻量级的守护进程
1 | cloudflared |
,由这个进程主动连接 Cloudflare 边缘网络,建立一条加密隧道。所有外部请求先到达 Cloudflare 边缘节点,再通过这条隧道转发到你的内部服务。
与传统的端口映射或 frp/ngrok 等内网穿透工具相比,Cloudflare Tunnel 有几个显著优势:
- 无需开放任何入站端口:服务器不需要监听公网端口,所有连接都是出站的,防火墙可以完全关闭入站
- 自动 HTTPS:Cloudflare 自动处理证书签发和续期,你不需要配置 Let’s Encrypt 或购买证书
- 隐藏源站 IP:外部用户看到的是 Cloudflare 的 IP,你的真实服务器 IP 永远不暴露
- DDoS 防护内置:流量经过 Cloudflare 边缘网络,自动获得企业级 DDoS 防护
- 访问策略控制:可以基于 IP、邮箱、SSO 身份等条件限制谁能访问
工作原理简述
整个数据流如下:
1
2
3
4 用户浏览器 → Cloudflare 边缘节点(Anycast)
→ 加密隧道(QUIC/HTTP2)
→ 你的服务器上 cloudflared 进程
→ 本地服务(localhost:port)
1 | cloudflared |
启动后会向 Cloudflare 的 4 个边缘 IP 发起出站连接(默认走 7844 端口的 QUIC 协议),建立多条持久连接以实现负载均衡和故障切换。即使某条连接断开,流量也会自动切换到其他连接,整个过程对用户透明。
环境准备与安装
前提条件
开始之前,你需要准备以下内容:
- 一个 Cloudflare 账户(免费版即可,Tunnel 功能免费可用)
- 一个已托管在 Cloudflare 的域名(NS 指向 Cloudflare)
- 一台 VPS 或内网机器(Linux x86_64 或 ARM)
- 需要暴露的本地服务(如 Web 应用、SSH、数据库等)
安装 cloudflared
在 Debian/Ubuntu 系统上,使用官方 APT 源安装:
1
2
3
4
5
6
7
8
9
10
11 # 添加 Cloudflare GPG key 和 APT 源
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | \
sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt-get update && sudo apt-get install -y cloudflared
# 验证安装
cloudflared --version
在 CentOS/RHEL 系统上,使用 yum 源:
1
2 sudo rpm -ivh https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-cloudflare-x86_64.rpm
cloudflared --version
也可以直接下载二进制文件,适合不想加源的场景:
1
2
3
4
5
6
7 # x86_64
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
sudo mv cloudflared-linux-amd64 /usr/local/bin/cloudflared
sudo chmod +x /usr/local/bin/cloudflared
# ARM64(树莓派等)
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64
创建隧道并暴露第一个服务
Cloudflare 提供两种方式创建隧道:命令行交互模式和 Dashboard 配置模式。推荐使用命令行模式,更灵活且便于自动化。
登录认证
首先进行认证,将
1 | cloudflared |
与你的 Cloudflare 账户绑定:
1 cloudflared tunnel login
执行后会输出一个 URL,在浏览器中打开它,选择你要使用的域名并授权。授权成功后,会在
1 | ~/.cloudflared/ |
目录下生成一个
1 | cert.pem |
文件,这是后续创建隧道的凭证。
创建隧道
1
2
3
4
5 # 创建一个名为 my-tunnel 的隧道
cloudflared tunnel create my-tunnel
# 输出示例:
# Created tunnel my-tunnel with id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
创建成功后,会生成一个 UUID 格式的隧道 ID,同时在
1 | ~/.cloudflared/ |
下生成对应的凭证文件
1 | <UUID>.json |
。这个文件非常重要,是隧道身份的唯一凭证。
编写配置文件
创建
1 | ~/.cloudflared/config.yml |
配置文件,定义路由规则:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 # ~/.cloudflared/config.yml
tunnel: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
credentials-file: /root/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json
ingress:
# 前端应用
- hostname: app.example.com
service: http://localhost:3000
# API 服务
- hostname: api.example.com
service: http://localhost:8080
# Grafana 监控面板
- hostname: grafana.example.com
service: http://localhost:3001
# 兜底规则(必须放在最后)
- service: http_status:404
每条 ingress 规则定义了一个域名到本地服务的映射。
1 | hostname |
是对外暴露的域名,
1 | service |
是本地服务的监听地址。最后必须有一条没有 hostname 的兜底规则。
配置 DNS 记录
使用以下命令自动在 Cloudflare DNS 中创建 CNAME 记录,指向隧道:
1
2
3
4 # 为每个 hostname 创建 DNS 记录
cloudflared tunnel route dns my-tunnel app.example.com
cloudflared tunnel route dns my-tunnel api.example.com
cloudflared tunnel route dns my-tunnel grafana.example.com
这个命令会自动在 Cloudflare DNS 中创建一条 CNAME 记录,指向
1 | <UUID>.cfargotunnel.com |
。你不需要手动去 Dashboard 添加 DNS 记录。
启动隧道
1
2
3
4
5 # 前台运行(测试用)
cloudflared tunnel run my-tunnel
# 后台运行
cloudflared tunnel --config ~/.cloudflared/config.yml run my-tunnel &
启动后,访问
1 | https://app.example.com |
就能看到你的本地服务了,而且自动带有 HTTPS。此时你的服务器没有开放任何入站端口——所有流量都是通过出站隧道进来的。
使用 Docker Compose 部署
在生产环境中,推荐用 Docker Compose 管理
1 | cloudflared |
,这样配置版本可控、重启自动恢复。首先准备好配置文件:
1
2
3
4 # 目录结构
mkdir -p ~/cloudflared/config
cp ~/.cloudflared/<UUID>.json ~/cloudflared/<UUID>.json
cp ~/.cloudflared/config.yml ~/cloudflared/config/config.yml
编写
1 | docker-compose.yml |
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 version: '3.8'
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --config /etc/cloudflared/config.yml run
volumes:
- ./<UUID>.json:/etc/cloudflared/<UUID>.json:ro
- ./config/config.yml:/etc/cloudflared/config.yml:ro
environment:
- TUNNEL_TOKEN= # 可选:使用 token 模式
networks:
- tunnel-net
# 如果需要访问宿主机上的服务,使用 host 网络或 extra_hosts
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
tunnel-net:
driver: bridge
在 Docker 环境中,
1 | localhost |
指向的是容器内部。如果你要代理的服务也跑在 Docker 里,把它们的 service 名称写进去即可(如
1 | http://web:3000 |
);如果服务跑在宿主机上,配置文件里把
1 | localhost |
改成
1 | host.docker.internal |
。
启动:
1
2 docker compose up -d
docker compose logs -f cloudflared
进阶配置:服务发现与负载均衡
Cloudflare Tunnel 支持基于 URL 路径的路由和多个后端实例的负载均衡,适合多节点部署场景。
基于路径的路由
1
2
3
4
5
6
7
8
9
10 ingress:
- hostname: app.example.com
path: /api/(.*)
service: http://localhost:8080
- hostname: app.example.com
path: /(.*)
service: http://localhost:3000
- service: http_status:404
这样
1 | app.example.com/api/* |
会转发到 API 服务,其余路径转发到前端应用。注意路径规则按从上到下顺序匹配。
多后端负载均衡
1
2
3
4
5
6
7
8
9
10
11
12 ingress:
- hostname: app.example.com
service: http://localhost:3000
originRequest:
connectTimeout: 10s
tlsTimeout: 10s
tcpKeepAlive: 30s
keepAliveTimeout: 90s
http2Origin: true
noTLSVerify: false
- service: http_status:404
如果需要真正的多后端负载均衡,可以在同一 hostname 下配置多个相同 service(Cloudflare 会轮询),或者使用 Cloudflare 的 Load Balancer 产品(付费功能)。
零信任访问策略
这是 Cloudflare Tunnel 最有价值的功能之一。你可以不依赖传统 VPN,通过访问策略精确控制谁能访问你的内部服务。
创建 Zero Trust 策略
在 Cloudflare Dashboard 中进入 Zero Trust → Access → Applications,添加一个自托管应用:
- 选择你的 Tunnel 域名(如
1grafana.example.com
)
- 配置认证策略,选择身份提供商(支持 Google、GitHub、SAML、OTP 邮箱等)
- 定义访问规则,如只允许特定邮箱域名、特定 IP 段访问
一个典型的策略配置:只允许公司邮箱且来自办公网络 IP 的请求:
| 规则字段 | 操作符 | 值 |
|---|---|---|
| 邮箱 | ends with | @yourcompany.com |
| IP 地址 | in range | 203.0.113.0/24 |
| 国家 | is | CN |
配置后,访问
1 | grafana.example.com |
会先跳转到 Cloudflare 的登录页面,验证身份后才能进入。服务本身不需要实现任何认证逻辑。
SSH 服务的隧道代理
Cloudflare Tunnel 也能代理 SSH 流量。在配置文件中添加:
1
2
3
4
5 ingress:
- hostname: ssh.example.com
service: ssh://localhost:22
- service: http_status:404
客户端需要配置
1 | cloudflared access ssh |
作为 SSH 的 ProxyCommand:
1
2
3
4 # ~/.ssh/config
Host ssh.example.com
ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
User your-user
这样即使你的 VPS 关闭了 22 端口的公网访问,仍然可以通过 Cloudflare 隧道 SSH 连接,而且可以叠加 Zero Trust 认证策略。
运维监控与故障排查
查看隧道状态
1
2
3
4
5
6
7
8 # 列出所有隧道
cloudflared tunnel list
# 查看隧道连接信息
cloudflared tunnel info my-tunnel
# 查看 cloudflared 进程日志
journalctl -u cloudflared -f --no-pager
常见问题排查
问题 1:DNS 解析不生效
检查 Cloudflare Dashboard 中 DNS 记录是否已创建,确认 CNAME 指向
1 | <UUID>.cfargotunnel.com |
,并且代理状态为橙色云朵(Proxied)。
问题 2:502 Bad Gateway
这通常意味着
1 | cloudflared |
能接收请求但无法连接到本地服务。检查配置文件中的
1 | service |
地址是否正确,以及本地服务是否正常运行:
1
2
3
4 # 在服务器上测试本地服务是否可达
curl -I http://localhost:3000
# 如果返回 200 说明本地服务正常,问题在配置文件
# 如果连接拒绝说明服务没启动或端口不对
问题 3:QUIC 连接超时
某些网络环境会封锁 UDP 7844 端口(QUIC 默认端口),可以强制使用 HTTP/2 回退:
1
2
3 # 在 config.yml 或启动参数中设置
# 启动时加 --protocol http2
cloudflared tunnel --protocol http2 run my-tunnel
或者在 config.yml 中全局设置:
1 protocol: http2
设置为系统服务
1
2
3
4
5
6
7 # 安装为 systemd 服务
sudo cloudflared service install
# 管理服务
sudo systemctl start cloudflared
sudo systemctl enable cloudflared
sudo systemctl status cloudflared
安装为系统服务后,配置文件路径变为
1 | /etc/cloudflared/config.yml |
,凭证文件也需要复制到
1 | /etc/cloudflared/ |
目录下。
安全加固建议
虽然 Cloudflare Tunnel 本身已经提供了相当好的安全基线,但在生产环境中仍有几点值得额外加固:
- 关闭所有入站端口:既然所有流量都走隧道,防火墙可以只允许出站。用
1ufw default deny incoming && ufw default allow outgoing
设置
- 限制 cloudflared 凭证文件权限:
1chmod 600 ~/.cloudflared/*.json
,防止其他用户读取
- 启用 Cloudflare WAF 规则:在 Dashboard 中开启防火墙规则,拦截常见攻击模式
- 配置速率限制:防止暴力枚举和 CC 攻击,在 Cloudflare 侧配置 Rate Limiting Rules
- 定期更新 cloudflared:
1cloudflared update
命令一键更新到最新版本
- 监控隧道断连:使用 Cloudflare 的 Notification 功能,在隧道离线时发送邮件或 Webhook 告警
成本与限制分析
Cloudflare Tunnel 的免费版对个人用户和中小项目完全够用:
| 项目 | 免费版 | 说明 |
|---|---|---|
| 隧道数量 | 无限制 | 可创建任意数量的隧道 |
| 带宽 | 无明确限制 | Cloudflare 不会硬性限制,但异常流量可能被审查 |
| DNS 记录数 | 1000 条 | 对绝大多数场景绑绑有余 |
| Zero Trust 用户数 | 50 人 | 免费版最多 50 个 Access 用户 |
| 并发连接 | 无硬性限制 | Cloudflare 建议单隧道不超过 1000 并发 |
如果需要更高规格的 SLA、更多 Zero Trust 用户、日志审计等功能,可以考虑 Cloudflare 的付费计划。但对于个人博客、小型团队内部工具、自托管服务等场景,免费版完全足够。
与 frp、ngrok 的对比
很多开发者可能已经在用 frp 或 ngrok 做内网穿透,下面做一个横向对比帮助你决定是否迁移:
| 特性 | Cloudflare Tunnel | frp | ngrok |
|---|---|---|---|
| 需要自有服务器 | 否 | 是(需公网 VPS) | 否(但免费版限制大) |
| 自动 HTTPS | 是 | 否(需自己配证书) | 是 |
| 隐藏源站 IP | 是 | 否(流量直达 VPS) | 是 |
| DDoS 防护 | 内置企业级 | 无 | 无 |
| 访问策略控制 | 内置 Zero Trust | 无(需自行实现) | 付费版有 |
| 免费版带宽 | 充足 | 取决于 VPS | 受限 |
| 自建依赖 | 无 | 需维护 frps 服务端 | 无 |
总体来说,如果你已经在用 Cloudflare 做 DNS,迁移到 Tunnel 几乎零成本。如果你目前在用 frp 且主要痛点是需要一台公网 VPS 作为中转,Cloudflare Tunnel 可以帮你省掉这台中转机器的费用和维护成本。
总结
Cloudflare Tunnel 把内网穿透、HTTPS 证书、DDoS 防护、访问控制这几件原本需要独立配置的事情整合到了一个方案里,而且核心功能免费。对于 VPS 用户来说,它特别适合以下场景:
- 家庭实验室或内网服务需要外网访问,但没有公网 IP
- VPS 上部署了多个服务,希望统一管理入口并隐藏真实 IP
- 需要给团队成员提供安全的远程访问,但不想搭建传统 VPN
- 想彻底关闭服务器入站端口,降低攻击面
部署完成后,建议你做一次完整的故障演练:手动停止
1 | cloudflared |
进程,观察服务是否自动恢复;检查 Cloudflare 是否在隧道离线时发送了告警通知。这些验证能确保在真正的网络故障发生时,你的服务能快速恢复。
如果你有多个地理位置分散的服务节点,还可以结合 Cloudflare 的 Load Balancer 和 Health Check 功能实现跨地域的高可用调度,这会是另一个值得深入探索的方向。
汤不热吧