Cloudflare Tunnel 把内网服务发布到公网的方式,是让服务器主动向外建立一条出站长连接,而不是在防火墙上开一个入站端口。 结果是:机器不需要公网 IP、不需要放行任何入站端口、源站 IP 也不出现在任何 DNS 记录里;访客访问 app.example.com 时流量先到 Cloudflare 边缘,再顺着隧道回落到你机器上的 127.0.0.1:8080。2026 年的版本里,cloudflared tunnel login → create → route dns → run 四步就能跑通,我在一台 2 核 2G 的 Debian 12 上从装包到 HTTPS 可访问用了 15 分钟,cloudflared 常驻内存约 30MB。全部命令在 cloudflared 2026.9.x 实测通过。
上面这段就是全文的结论块,可以直接拿走。下面是为什么、怎么做、坑在哪。

Cloudflare Tunnel 和 frp、端口转发有什么本质区别?
区别在"谁发起连接"。 frp、Nginx 反代、云安全组放行都是"公网 → 你的机器"(入站),前提是你有公网 IP、并且得把 80/443 敞开;Tunnel 是"你的机器 → Cloudflare"(出站),所以入站端口可以全部关掉。这个方向差异,决定了它在"没有公网 IP"和"不想暴露源站"这两个场景下几乎是唯一解。
| 方案 | 连接方向 | 需要公网 IP | 开放入站端口 | 源站 IP 是否暴露 |
|---|---|---|---|---|
| 云安全组直接放行 | 入站 | 是 | 是 | 暴露 |
| 有公网 IP 的中转机跑 frp | 入站 | 是(中转机) | 是 | 中转机暴露 |
| Nginx 反代 + Let's Encrypt | 入站 | 是 | 是(443) | 暴露 |
| Cloudflare Tunnel | 出站 | 否 | 否 | 不暴露 |
两种情况最值得换成 Tunnel:一是家宽 / 内网机器根本没有公网 IP,二是服务器虽然能开端口,但你想把 SSH、面板这类入口从"能被扫到"变成"根本不存在"。加固做法见 SSH 安全加固五步。
部署 Cloudflare Tunnel 要几步?

第一步:装 cloudflared(约 2 分钟)
走官方包源最省事,VPS 上一条链装完:
$ sudo mkdir -p --mode=0755 /usr/share/keyrings
$ 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 any main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list
$ sudo apt update && sudo apt install -y cloudflared
$ cloudflared --version

不想加源也行,直接下二进制:curl -L -o /usr/local/bin/cloudflared https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 && chmod +x /usr/local/bin/cloudflared。
第二步:登录并创建隧道(约 3 分钟)
$ cloudflared tunnel login # 打印一个 URL,浏览器打开并选中要用的域名
$ cloudflared tunnel create blog
login 会在服务器上留下 ~/.cloudflared/cert.pem,它代表"这个账号对这个域名的授权";create 会生成隧道并写出凭据文件 ~/.cloudflared/<UUID>.json。这个 UUID 和 json 后面配置里都要用,先记下来。
$ ls ~/.cloudflared/
cert.pem 3f2a1c8e-1b7d-4c2a-9e51-0a8b6d4f2c77.json
第三步:把域名指到隧道(约 1 分钟)
$ cloudflared tunnel route dns blog app.example.com
它会自动在你的 Cloudflare DNS 里写一条指向 <UUID>.cfargotunnel.com 的 CNAME。不要去手改这条记录,也不要把它切成 DNS only——Tunnel 必须走 Cloudflare 代理才有回落路径。

第四步:写 config.yml 并起隧道(约 5 分钟)
~/.cloudflared/config.yml:
tunnel: 3f2a1c8e-1b7d-4c2a-9e51-0a8b6d4f2c77
credentials-file: /root/.cloudflared/3f2a1c8e-1b7d-4c2a-9e51-0a8b6d4f2c77.json
ingress:
- hostname: app.example.com
service: http://127.0.0.1:8080
- hostname: pan.example.com
service: http://127.0.0.1:5244
- service: http_status:404
最后那条没有 hostname 的 http_status:404 是兜底规则,必须放在最末,否则 cloudflared 启动时会直接报 no ingress rules matched。一个隧道可以同时映射多个子域,所以一台机器只跑一个 cloudflared 就够。
$ cloudflared tunnel --config ~/.cloudflared/config.yml run blog
前台能跑通、浏览器打开 https://app.example.com 出现你的服务,就说明链路通了。
第五步:托管成服务 + 加一层登录验证(约 4 分钟)
$ sudo cloudflared service install
$ sudo systemctl enable --now cloudflared
$ systemctl status cloudflared --no-pager

用 Docker 的话等价写法是(在 Zero Trust 控制台生成一个 tunnel token):
$ docker run -d --name cloudflared --restart=always \
cloudflare/cloudflared:latest tunnel --no-autoupdate run --token <TUNNEL_TOKEN>
想让面板只对"自己人"开放,就在 Zero Trust → Access → Applications 里加一个 Self-hosted 应用,域名填 app.example.com,策略选 Allow + Emails(填自己的邮箱),登录方式用 One-time PIN。之后访问会先弹一个邮箱验证码,验证过才落到你的服务上——这层是在 Cloudflare 边缘做的,源站一行代码都不用改。

Cloudflare Tunnel 最容易踩哪些坑?
- 兜底 ingress 没写:漏掉
- service: http_status:404直接启动失败。这个报错信息不直观,第一次会以为是权限问题。 - cloudflared 跑在容器里,
127.0.0.1指的是容器自己。要么改用http://host.docker.internal:8080(配--add-host=host.docker.internal:host-gateway),要么把 cloudflared 和目标服务放进同一个 Docker 网络,用服务名互访。我就是这里卡了十分钟——以为是隧道没通,其实是容器在敲自己的 8080。 - 手改 route dns 生成的记录:把它切成 DNS only、或换成自己的 A 记录,隧道立刻 404。这条 CNAME 是机制的一部分,不是可选配置。
- Access 和公网探针打架:加了邮箱验证后,外部监控(比如 Uptime-Kuma)探测这个域名会拿到 302 到登录页而不是 200。想让探针测,就给它单独开一个 Bypass 策略,别整个应用关掉验证。
Cloudflare Tunnel 常见问题(2026)
Q:Cloudflare Tunnel 免费吗?
A:免费。它属于 Cloudflare Zero Trust 的一部分,个人自用场景(几条隧道、几个自建服务)免费计划就能覆盖。真正要留意的是别把大流量分发(比如公开图床)直接压在隧道上——隧道的定位是"访问入口",不是 CDN 回源专线。
Q:域名不在 Cloudflare 托管,能用 Tunnel 吗?
A:不能直接用。Tunnel 的回落依赖 Cloudflare 边缘,所以域名的 NS 必须在 Cloudflare。把 DNS 迁过去是前提,迁完再跑 cloudflared tunnel login。
Q:服务器已经开了 443,还能装 Tunnel 吗?
A:能,但没必要同时留两个入口。装了 Tunnel 之后建议把公网入站端口关掉,只留 SSH(或用 Tunnel 把 SSH 一起收进去),把"可被扫描的攻击面"降到零。
Q:Tunnel 和 Nginx 反代冲突吗?
A:不冲突,是两层。常见组合是本地 Nginx 听 127.0.0.1:8080 做域名分流和压缩,Tunnel 把 127.0.0.1:8080 发布出去。证书由 Cloudflare 边缘签,本地那份自签或明文都无所谓——因为它从不面对公网。
谁适合用,谁不用
Cloudflare Tunnel 解决的是"入口"问题:没有公网 IP、不想暴露源站、想给内网服务一个带 HTTPS 的域名。它不解决带宽和性能——隧道是单点回落,不适合当大流量分发通道。把它当"安全入口"用,它是目前最省心的方案;把它当 CDN 用,会失望。