只要在本地写过 Web 应用、给同事演示过内网工具,或者试过用家里的旧电脑搭一个小站点,大概率都会碰上同一个现实问题:人没有公网 IP,运营商给的是大内网地址,路由器端口映射改得又不痛快。这时候 Cloudflare Tunnel 的常用命令就成了很多人的救命稻草——一条cloudflared tunnel --url就能把本地的 8080 端口变成公网能访问的 HTTPS 地址,听起来确实很爽。
但在网上搜“Cloudflare Tunnel 常用命令”,画风很容易变成“抄命令”。命令是有限的,坑是无底的。我见过有人在群里问,cloudflared tunnel run一会儿 502 一会儿连接重置,实际验证之后发现,他的本意是想把两台机器上的不同服务暴露出去,却因为没弄清命名隧道、ingress 和快速隧道的分工,硬是把自己绕进了配置地狱。这个问题的种子,就是经典的 XY 问题:“你问的是你自己拟的方案,丢的却是真正要解决的目标场景。”
Cloudflare Tunnel 日常到底要掌握哪些命令?什么场景下用临时隧道,什么场景用命名隧道?配置文件里的 ingress 规则是怎么按序匹配的?我把自己的落地经验整理了一遍,中间穿插了一些 XY 问题的避坑反思。希望你看完,能像一个老手那样,掐着需求直接选对命令。
1. 在背命令之前,先解开"XY问题"那层结
1.1 所有常用命令都指向同一个目标:把本地服务安全地"递"出去
Cloudflare Tunnel 用到的命令其实绕不开四件事:登录认证(login)、建立隧道(create)、建立 DNS 路由(route),最后再运行(run)把流量接进本地。这些操作的核心只有一句话——在你的电脑(没有公网 IP 的局域网设备也可以)上主动连出一根出站长连接,接到 Cloudflare 的全球边缘网络;用户访问你的域名时,请求先落到 Cloudflare 边缘,再由边缘通过这条透明通道,把请求交给那台仍在运行的 cloudflared 进程,最后由它转发到本机目标端口。全程不需要对外开放入站端口,不用改路由器端口映射,也不需要在云厂商买一台“中转机”。
用五步来理解这个链路:
- 本地跑一个 Web 服务,监听 8080 端口。
- 运行 cloudflared 进程,与 Cloudflare 边缘建立安全的反向连接。
- 在 Cloudflare 为域名配置一条 DNS 记录,或者用快速隧道自动生成的子域。
- 边缘将对应域名的 HTTPS 请求,安全地转交给 cloudflared 进程。
- cloudflared 将请求转发回本地 8080 端口,再把响应原路返回。
把这个模型记在脑子里以后,你再去看“常用命令”就不是背 API 了,而是在完成这五步里的每一块。命令的输出信息也很关键:创建成功会输出隧道 ID,路由时会显示 DNS 记录是否创建成功,这些就是后续稳定运行的锚点。
1.2 当你问"这个命令怎么不行"的时候,先说清楚你的真实需求
XY 问题在 cloudflared 社区里尤其常见,因为隧道本身是个底层工具,很多人会习惯性把“我认为应该怎么搭”当成问题抛出来。
- 有人问“怎样让 cloudflared 同时支持 443 和 8090 两个端口”,他真正需要的其实是想让两个不同子域的服务共用同一个本机入口,该做的是 ingress 配置,而不是研究端口复用。
- 有人问“为什么我用
--url开出来的隧道,重启电脑就找不回来了”,这本来就是临时演示的机制,他真正的需求是需要一个长期稳定的入口,该用命名隧道而不是快速隧道。 - 有人问“为什么我的隧道跑一会儿就断”,最后发现本地开发服务器的进程因为热重载把端口换了,隧道本身没有问题。
所以遇到 Cloudflare Tunnel 问题,第一步先问自己:“我想达成的最小可接受结果是什么?”如果答案是“把某个本地服务暴露到公网并绑定我自己的域名”,那就可以直奔命名隧道 + ingress 的稳妥组合;如果答案是“别人只看一次我本地跑的效果”,快速隧道已经够用。对着 X 选方案,而不是对着 Y 修参数,后面所有命令都会顺很多。
我把常见的伪问题整理成一张对照表,方便你自查:
| 表面问题(Y) | 真实需求(X) | 对症方案 |
|---|---|---|
| 为什么端口 8080 老是冲突? | 我想让两个子域都访问本机 | 写一份 ingress,按 hostname 分流 |
| 快速隧道重启后地址就变了? | 我需要一个固定地址长期可用 | 改名称为命名隧道,走 route dns |
| run 的时候日志刷 502? | 本地对应端口根本没有可用服务 | 先用 curl 验证本地服务,再查隧道 |
| 想在同一台机器跑多个隧道? | 其实只要一个隧道 + 多级映射 | 用 ingress 配置多域名转发 |
2. 快速隧道:一条命令就能跑起来,但要认清它的"一次性"本质
2.1 先跑通的第一个命令:cloudflared tunnel --url
快速隧道是最简单的入口,适合第一次接触 cloudflared 的人。先安装二进制文件,Linux 上我通常直接下载 release 包:
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared chmod +x cloudflared sudo mv cloudflared /usr/local/bin/macOS 用户可以直接brew install cloudflared,Windows 则下载对应的.exe放到 PATH 目录里。装好后,假设你的本地服务跑在 8080 端口:
cloudflared tunnel --url http://localhost:8080输出里会给你一行类似https://xxxxx.trycloudflare.com的地址,浏览器打开就能看到你的页面。这个过程中不需要登录 Cloudflare,不需要域名,不需要改 DNS,速度也很快,非常适合第一次验证“我的本地服务能不能被外网访问”这件事。
这里有个小坑:我建议 URL 写成http://127.0.0.1:8080,而不是localhost。某些环境下localhost会被解析成 IPv6 的::1,而你的服务只监听了 IPv4,就会出现一种诡异的“本地能开、隧道那边 502”。用 127.0.0.1 可以把这种变量直接抹掉。
2.2 为什么说是"一次性":限制和适用场景
快速隧道的主要限制有四个:
- 域名是随机生成的,不可自定义,也没法在 Cloudflare 面板里提前绑定。
- 每次运行产生的子域都不同,你只能在当前进程的生命周期里使用它。
- 进程一停,链接就失效,下次启动又是一个新地址。
- 它本质上是 Cloudflare 给你一个无需账号就能体验的沙箱,适合短时联调,不适合对外长期交付。
合适的用法是:给客户演示某个页面、在手机上打开本机调试的 H5、跟 Webhook 对接方做十分钟联调。这类“看一次就走”的场景,快速隧道是效率最高的选择。但如果你的目标是让同事每天都能访问,或者绑定自己的域名,那就要往前再走一步,用命名隧道。
2.3 快速隧道不适合裸奔生产,也别把它当反向代理
有人觉得快速隧道都是 HTTPS,安全性应该还行。但实际上它没有任何访问控制,只要 URL 泄露,谁都能访问你的本地服务。真要拿给团队内部用,建议要么在应用层加登录鉴权,要么直接转命名隧道,配合 Cloudflare Access 做一层身份校验。把公网可见面控制在你期望的范围内,这是一个基本素养。
3. 命名持久隧道:login → create → route → run 的完整命令链路
命名隧道是生产环境的常态。它会给隧道分配一个固定的 ID,凭据写进一个 JSON 文件,再通过 Cloudflare 的 API 把域名 DNS 记录绑定好。之后进程重启多少次,域名都不会变。整套流程可以拆成四步。
3.1 第一步:login 认证
cloudflared tunnel login第一次运行会打开浏览器,让你在 Cloudflare 登录授权页选一个账号并有域名管理权的 zone 进行授权。授权完成后,会在本机生成~/.cloudflared/cert.pem,这张证书负责后续的“创建隧道、绑定 DNS、删除隧道”等管理操作。
这里有几个容易忽略的点:
cert.pem只用于管理操作,不用于运行时验证。运行时靠的是下一步生成的 JSON 凭据文件。- 它不是账号密码,但丢了之后很多管理命令会失效,比如无法再修改隧道配置。
- CI 环境里没法弹窗交互,建议在本地跑一次,把证书文件一起带到 CI 使用。
3.2 第二步:create 创建隧道
cloudflared tunnel create demo-tunnel成功后会显示:
Created tunnel demo-tunnel with id f0a4e5df-xxxx-xxxx-xxxx-xxxxxxxxxxxx Credentials file created at /root/.cloudflared/f0a4e5df-xxxx.json这个 JSON 文件就是运行时最重要的资产,里面包含隧道 ID 和凭据。一旦丢失,即使你在 Cloudflare 面板上能看到隧道列表,也无法再完成后续操作。我的习惯是:create 成功之后立刻把 JSON 备份到安全位置,比如私有 Git 仓库、NAS 或密码管理器。删除隧道时再同步删除备份文件,避免留下一堆失效凭据。
3.3 第三步:route DNS 把域名关联到隧道
命名隧道不会自动帮你创建域名记录。你需要显式执行:
cloudflared tunnel route dns demo-tunnel app.example.com它会在你的 zone 里自动创建一条 CNAME 记录,指向<tunnel-id>.cfargotunnel.com。如果你的域名本来就托管在 Cloudflare,这条命令执行完,DNS 层就通了。如果域名在其他服务商,就需要手动去那边添加 CNAME,并确保在 Cloudflare 侧开启代理状态。
这里常有人产生误解:以为 route dns 之后,域名立刻就能访问本地服务。其实不对。route dns 只解决“域名 → 隧道”的 DNS 映射,真正要把请求转发到本机哪个端口,需要第 4 节的 ingress 配置来完成。
3.4 第四步:run 启动隧道
先准备好配置文件,然后启动:
cloudflared tunnel run demo-tunnel启动成功会看到Registered tunnel connection类似的日志,这时浏览器访问app.example.com,正常情况下已经能到达本地服务。如果出现 502,先回本机 curl 一下端口,排除服务本身的问题。
一个很容易混淆的点:有人以为 run 命令会占用一个本机端口,于是去查端口有没有被占用。其实 run 是出站连接,它不需要监听本机端口,除非你在配置里特意暴露另一个入口。所以“端口被占”很多时候不是隧道的错,而是把快速隧道的--url参数和命名隧道的配置机制搞混了。
3.5 巡航查账:list / info / delete / cleanup
运维期最常用的命令我列在这里:
| 命令 | 用途 |
|---|---|
cloudflared tunnel list | 查看当前账户下的隧道列表、是否在运行 |
cloudflared tunnel info demo-tunnel | 查看单个隧道 ID、连接信息 |
cloudflared tunnel cleanup demo-tunnel | 清理因异常退出残留的陈旧连接记录 |
cloudflared tunnel delete demo-tunnel | 删除隧道记录,仍在运行时需要加-f |
cloudflared tunnel --help | 任何命令记不牢时的通用救法 |
一个绕不开的坑:delete之后,之前的 DNS CNAME 不会自动删干净。你需要再去面板或手动删掉那条指向<uuid>.cfargotunnel.com的 CNAME,否则域名会出现悬空解析。这条最好写进你的运维 checklist,老手也容易忘。
4. 多域名多服务下的 ingress 配置文件到底该怎么写
命名隧道最有价值的部分,就是把多个域名、多个服务整理成一个可维护的配置文件,也就是 ingress。这里是很多人卡住的地方,也是 XY 问题的高发区。
4.1 ingress 规则的本质:一张按顺序匹配的“路由表”
cloudflared 在转发请求之前,会先读取默认路径的~/.cloudflared/config.yml,也可以在你运行隧道的目录放一份,或用cloudflared tunnel run --config /some/path/config.yml显式指定。然后根据请求的 Host 头依次做匹配。
一个典型配置:
tunnel: demo-tunnel credentials-file: /root/.cloudflared/f0a4e5df-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json ingress: - hostname: blog.example.com service: http://127.0.0.1:8080 - hostname: api.example.com service: http://127.0.0.1:9090 - service: http_status:404- 第一段声明隧道名是
demo-tunnel,运行时 cloudflared 会拿它和credentials-file里的 ID 配对。 - 然后配置 blog 子域转发到 8080 端口,api 子域转发到 9090 端口。
- 最后一条是一条兜底规则:没有
hostname字段,表示其他未匹配的请求都返回 404。没有兜底的话,一些意外走到该隧道的域名就会造成行为不明确。
关键点:ingress 规则按从上到下的顺序匹配,匹配成功即生效。如果你把兜底规则写在最上面,所有流量都会落入兜底,后面的规则形同虚设。很多人以为问题是端口冲突,结果其实是规则排序写错。
4.2 service 字段支持的几种类型
根据不同协议需求,service 字段常见几类:
| 值 | 含义 |
|---|---|
http://127.0.0.1:8080 | HTTP 协议转发到本地端口 |
https://127.0.0.1:8443 | HTTPS 协议转发到本地端口 |
unix:///var/run/app.sock | 转发到本地 Unix socket |
http_status:404 | 只响应给定状态码,不做版转发,适合兜底 |
在此基础上,你还可以在规则里加originRequest子字段,设置超时、重写 Host 头等。对大多数项目来说,先把前两种和兜底规则吃透,已经能覆盖 90% 的“本地服务暴露为公网子域”需求。
4.3 配完别动手就 run,先 validate
配置文件有语法错误时,直接 run 也能启动,但请求会乱跳。更稳妥的做法是先校验:
cloudflared tunnel ingress validate如果配置正确,会输出Valid ingress rules;如果哪行 hostname 没匹配、凭据文件找不到、YAML 缩进错误,提示会直接告诉你问题大概在哪一行。这条命令的成本极低,但能把“配完直接上生产”的翻车率降下来一大截。
我现在的习惯是:先写一个最简单的单域名、单服务配置,跑通之后再复制规则扩展更多域名。加规则永远比一开始就搭一套复杂结构要稳。大型项目几十个域名,也是同样原理,只是会把配置文件放进 Git 仓库,用版本管理维护差异和回滚。
5. 我在落地过程中踩过的坑(命令背后的雷区)
标准命令讲完了,这一段才是真正没法在文档里找到、靠一次次扑腾攒下来的经验。
5.1 502 半天查不出问题,root cause 通常在“服务监听地址”
有一次我用快速隧道给一个本地调试服务做演示,结果 502 了十几分钟。排查到最后,发现本机上有一个残留进程占用了同一个端口,返回 500,Cloudflare 边缘拿到 500 状态码之后又给了 502。问题根本不在 cloudflared,而在于“目标端口背后的服务到底是谁”。
后来我养成了一个习惯:先从本机用curl请求目标端口,确认返回内容符合预期,再让 cloudflared 转发进去。虽然只是多了一步,但它能把“本地服务的问题”和“隧道链路的问题”迅速分离。看到Origin unreachable类似的日志,先查本地进程与端口,再查 cloudflared 日志,最后才看 DNS 与网络,这个顺序不会错。
5.2 凭据文件丢失,数据还在但钥匙没了
我重装机器时忘记备份~/.cloudflared/<tunnel-id>.json,后来cloudflared tunnel list还能看到隧道记录,心想着应该还能管理。结果发现,任何需要验证身份的操作都离不开那串 JSON,没了它,只能去面板删除隧道记录重建,域名 CNAME 也跟着重建。
这件事让我确立了几个很硬的原则:
- create 之后第一时间把 JSON 放到可信的备份位置。
- 配置化部署时,把 JSON 当密钥处理,别明文进代码库。
- 换机器时,把
cert.pem和 JSON 一起带过去,绑定关系才完整。
5.3 DNS 悬空或 route 冲突
前面提过删隧道不删 CNAME 的问题,这里再补一条更隐蔽的:使用route dns时,如果域名已经存在一条其他 CNAME,命令可能会直接失败,或者覆盖掉你想保留的解析记录。真正常见的 XY 表现是“我想让一个子域同时给两台服务器做负载均衡”,于是非要通过改隧道入口来实现。其实正确路径是交给 Cloudflare Load Balancer 或专门的负载层设备,隧道本身并不承担高级流量调度。先看清需求边界,才不会自己给自己挖坑。
5.4 开机自启与 systemd 的日志闭环
命令在终端里跑着都能用,但一关终端整个隧道就没了,这很正常。要长期运行,我更倾向于用官方封装的服务安装方式:
sudo cloudflared service install它会按/etc/cloudflared/config.yml读配置并交给系统服务管理,启动和崩溃重启都由系统托管。安装完之后,确认一下:
systemctl status cloudflared journalctl -u cloudflared -f这套组合的好处是日志有迹可循,不用在裸终端里开一个夜班窗口。如果你非要手写 systemd unit,记得把 User、ExecStart、配置文件路径全部写完整,否则很容易出现“手动能跑、服务起不来”的怪事。
6. 剥开命令外壳后,真正要回答的其实是"我用它做什么"
把 Cloudflare Tunnel 的常用命令一层层拆开之后你会发现,它既不神秘也不复杂。复杂的是每个人把它放到怎样的场景里、给谁访问、需要什么样的稳定性。把这个过滤布做好,很多看似绕弯的命令都会自动消失。
6.1 动手前回答五个问题
- 这个服务的访问者是固定几个人,还是全网公开?
- 需要绑定固定域名和 HTTPS 证书吗?
- 是需要长期 7x24 运行,还是只活一次联调?
- 本地进程监听在哪个端口,能否接受 cloudflared 访问?
- 如果隧道挂了,你能接受几秒不可用,还是需要自动重启?
按这五个问题的答案走,你基本会自动得出以下命令组合:
- 临时公开目测:
cloudflared tunnel --url http://127.0.0.1:PORT - 永久稳定暴露:先执行
cloudflared tunnel login、cloudflared tunnel create NAME、cloudflared tunnel route dns NAME DOMAIN,再写好 ingress 配置并cloudflared tunnel run NAME - 机器重启后仍自动恢复:最后加一步
sudo cloudflared service install
6.2 我的个人经验:先在自己的服务层跑通,再让 cloudflared 做最后一层传递
如果你想更进一步,我的做法是:先把应用服务本身在本地跑顺,再用 nginx 这类工具把“域名 → 端口”的映射提前配好,最后让 cloudflared 把公网流量递交给这台 nginx。这样一来,遇到 502,你能很快把问题圈定在“cloudflared 之前”还是“cloudflared 之后”,而不是在几条命令之间反复试探。
这其实也是 XY 问题的主旨:要多问“为什么这条请求没有到达我预期的地方”,而不是纠结“某条命令是不是应该背得更熟”。命令可以随时查,但对工具分层和依赖关系的理解,才是在真实业务里能落地的根。