内网穿透零成本实战:用cloudflared从临时隧道到固定域名
折腾过内网穿透的人,基本都经历过一个死循环:用 ngrok,免费版 URL 每次都变,还要注册 token;用 frp,先得有一台带公网 IP 的服务器,一年下来服务器钱省不掉;用 cpolar,免费隧道限制又不少。后来我把 cloudflared 用起来以后,才意识到内网穿透这件事真没必要搞那么痛苦——不需要自己买服务器,不需要申请公网 IP,免费,一条命令就能把本地服务拉到公网。这篇文章会把我这大半年用 cloudflared 的经验整理出来:先讲为什么它能做到“零成本”,再讲从临时隧道到固定域名隧道的完整操作流程,最后把我踩过的坑、维护中遇到的问题一并交代清楚。适合家里有 NAS、喜欢自建服务,或者经常需要给客户演示本地程序的开发者参考。
1. 折腾过ngrok和frp之后,为什么把cloudflared留下
1.1 主流内网穿透方案的AB面
先说我自己的需求背景:家里有一台 NAS,上面跑了几个 Web 应用、一个 git 服务,偶尔还要给朋友演示一些刚写完的页面。以前我用的方案是 frp,因为那时候觉得 frp 可控性最强,配置灵活,协议也开放。但 frp 有个绕不开的前提:你得有一台带公网 IP 的服务器做中转。我买了一台轻量云服务器,一年三四百块,流量和带宽还有上限,每次流量超了我都要心疼一阵。
后来我还试过 ngrok,它的 SaaS 模式确实省事,不用自己维护服务器,下载客户端就能用。但免费版的限制很让人难受:启动时分配给你的域名是随机的,想固定域名就要付费,而且免费隧道有连接数限制,临时给同事看个页面还行,真正想持续对外提供服务就显得不够用。cpolar 我也用过,国内访问速度确实好一些,但免费档限速、限域名,用着用着就会发现核心功能都在付费墙后面。
下面这张表是我当时对比的结论,供你参考:
| 方案 | 需要自己的服务器 | 需要公网IP | 免费可用程度 | 固定域名成本 |
|---|---|---|---|---|
| frp | 是 | 是 | 免费,但要自建 | 自备域名+服务器 |
| ngrok | 否 | 否 | 有限制,URL随机 | 付费 |
| cpolar | 否 | 否 | 有限制,限速限域名 | 付费 |
| cloudflared | 否 | 否 | 基本免费 | 自备域名,免费绑定 |
cloudflared 最让我意外的点,是它居然把“免费”这个选项给做完整了。不需要自己的服务器,也不要求你把端口暴露到公网,甚至一开始连账号都不用注册就能跑起来。这种“零门槛”特性,在我只是想快速验证一个本地服务时非常有用。
1.2 cloudflared真正的差异:不是隧道,是整套边缘网络
Cloudflare 这家公司本身就在做 CDN、DNS、安全防护,它的 Tunnel 产品(以前叫 Argo Tunnel)并不是一个独立的小工具,而是 Cloudflare 边缘网络的一部分。这意味着 cloudflared 不只是帮你把流量穿透出去,它天然整合了三样东西:DNS 解析、免费 SSL 证书、访问控制策略。
举一个非常实际的例子:我用自己的域名 example.com 做穿透时,只需要在 Cloudflare 上把 nas.example.com 解析到隧道,Cloudflare 自动就给这个域名签发 TLS 证书,我本地应用完全不用处理 HTTPS,访问者拿到的是一个带锁的网址。这个体验在 frp 模式里很难做到——用 frp 时你通常要自己在服务器上配 Nginx、配证书、处理反代,链路越长出问题的概率越大。
而 cloudflared 把这一整条链路压缩成了一句话:cloudflared tunnel --url http://localhost:8080。它是我见过的唯一一个把“穿透”和“边缘服务”放在一起讲的产品。这也是为什么我最终放弃了自建 frp 服务器,把大部分内网服务都迁到了 cloudflared 上。
2. 一条命令穿透,背后做了什么
2.1 出站连接:没有公网IP也能让公网访问的关键
很多人第一次看到 cloudflared 都会疑惑:没有公网 IP,没有公网入口,公网用户到底怎么访问到我的本地服务?关键在于一个概念:出站连接。
frp 这类传统方案是“服务器中转”:你的内网机器主动连到你的公网服务器,公网用户也连到那台服务器,服务器把两边的数据互相转发。所以它必须有一台公网服务器作为固定中继点,流量走的路径长,带宽也受限于那台服务器。
cloudflared 走的是另一条路:你机器上的 cloudflared 进程主动向 Cloudflare 的边缘节点发起一条长期的出站连接,这条连接建立后,Cloudflare 就像是你的“总机”。公网用户访问你的域名时,请求先到 Cloudflare 边缘节点,再由边缘节点通过这条已经建立的长连接,把请求原封不动地送到你的本地服务。
用一个类比来说:frp 像是你每次都要打一台固定电话转接,而 cloudflared 是你和总机之间已经拉好了一条内线,总机收到外线电话后直接通过内线转给你。因为你机器发起的是“主动外呼”的连接,所以不需要公网入口,没有公网 IP 也完全能跑。这个设计是全区穿透工具里最干净利落的一类思路。
2.2 免费额度与速度的实际体验
对于免费套餐,Cloudflare 没有按流量计费,也没有像 ngrok 那样强制限制连接数让你升级会员。个人使用场景下,我实测下来是够用的:本地 200M 家庭宽带,走 cloudflared 的话,传输小文件、看个网页、传个 API 请求,体感和直连差别不大。但要注意,免费套餐不保证带宽,晚高峰或跨地区访问时延迟会明显上升,毕竟是经过 Cloudflare 边缘节点中转。
所以我的建议是:适合用来跑 Web 管理面板、API 调试、远程桌面、临时给客户演示这类低频交互服务。不适合拿去跑视频流、大文件长时间传输,或者对延迟极其敏感的实时服务。免费额度再香,也扛不住当 CDN 用。我一般把 cloudflared 定义成“轻量服务的统一入口”,而不是万能加速器。
3. 从零跑通第一条临时隧道
3.1 安装与版本确认
cloudflared 的安装方式很常规,Linux 下可以直接下载官方二进制,也可以走包管理器。我用的是 Debian 系,最省事的方式是下载 deb 包安装:
macOS 用户可以直接 brew install cloudflared,Windows 用户下载官方 msi 安装包即可。装完以后先用 cloudflared --version 确认版本号,后面排错时这个数字很重要——不同版本的日志和参数有一些细微差别,排查问题时先对版本。
3.2 一条命令起隧道,然后看看日志在说什么
装好之后,先用最简单的方式感受一下穿透效果。随便起一个本地服务,比如 Python 自带的 HTTP 服务:
然后另开一个终端,执行:
cloudflared 会做这几件事:连接 Cloudflare 边缘节点、创建一条临时隧道、给你分配一个 trycloudflare.com 结尾的随机域名。启动成功的日志里会打印类似这样的内容:
把这个网址在浏览器里打开,如果你能看到刚才本地 HTTP 服务的内容,说明第一条隧道已经通了。这个临时隧道完全不需要注册账号,也不用绑定域名,非常适合应急演示。
这里要注意一个特点:quick tunnel 是随机域名,每次重启都会变。它没有登录认证,拿到链接的人都能访问,所以临时演示完最好直接把进程停掉,不要把临时链接发出去后就不管了。
3.3 临时隧道怎么常驻:systemd与Docker两种姿势
临时隧道虽然方便,但前台挂着进程总不是长久之计。我一般会用 systemd 把它做成服务,断线自动重启:
如果你习惯用容器,也可以直接跑官方镜像:
在 Linux 上如果希望容器穿透宿主机服务,记得把 host.docker.internal 换成 --network host 的方式,或者让容器直接连到同一个 Docker 网络里的其他容器服务名。这个细节我最初踩过坑,后面会在踩坑章节细说。
4. 固定域名与访问控制:命名隧道才是日常主力
4.1 临时隧道只能应急,固定域名需要命名隧道
quick tunnel 适合演示,但你要在家里长期跑一个服务,让域名固定、让访问可控,就必须升级到命名隧道。命名隧道和临时隧道的核心差别在于:你可以把隧道的名字和域名绑定,把流量按 hostname 分发到不同的本地服务,还能用 Cloudflare Access 给这些域名加访问策略。
前置条件是你有一个域名,并且把域名的 DNS 托管到 Cloudflare。这个操作在 Cloudflare 官网添加站点就能完成,免费套餐就能托管。如果域名没有托管到 Cloudflare,命名隧道的 DNS 路由功能就用不了,这是 cloudflared 和其他内网穿透工具一个不太一样的绑定点。
4.2 命名隧道的完整操作流程
我以 home 这条隧道为例,把完整流程走一遍。首先是登录授权:
这个命令会打开浏览器,让你选择授权的域名。授权成功后会在 ~/.cloudflared/ 下生成一个 cert.pem,这个文件是你管理隧道的凭证。
然后创建隧道:
创建成功后会输出一串 Tunnel ID,并在 ~/.cloudflared/ 下生成一个 <TUNNEL-ID>.json 凭证文件。这个名字对应的就是隧道 ID,后面配置文件里要用到。
接着把域名路由到这条隧道上:
这一步会在 Cloudflare 的 DNS 记录里自动添加一条 CNAME,指向 home 这条隧道。以上做完后,写配置文件 ~/.cloudflared/config.yml:
注意 ingress 规则是顺序匹配的:Cloudflare 收到请求后,会从上往下找第一个匹配 hostname 的规则,把流量转发到你指定的本地地址。最后一条 service: http_status:404 不加 hostname,作为兜底规则,否则配置文件校验会报错。规范上是强制要求有兜底规则的。
启动命名隧道:
也可以指定配置文件启动:
跑起来之后,访问 https://nas.example.com,应该就能看到你 localhost:5000 的服务。到这里,你已经把多个本地服务合并到一条隧道后面,每个域名对应一个服务,后续想加服务只需要在 ingress 里加一条规则然后重启隧道。
4.3 用Cloudflare Access给公网入口加锁
命名隧道默认是“公开”的,任何知道域名的人都能访问。如果你的本地服务本身没有登录机制,这一步非常关键:用 Cloudflare Access 给入口加一道鉴权。
操作入口在 Cloudflare Dashboard 的 Zero Trust 区域,路径是 Access -> Applications,然后 Add an application,选 Self-hosted。填入你的域名(比如 nas.example.com),接着创建一条策略,策略类型选 Allow,规则里指定允许访问的邮箱域名,比如 @gmail.com 或你自己的邮箱。
配置好之后,未授权用户访问你的隧道域名时,会看到 Cloudflare 的阻止页面,而不是直接暴露你的服务。免费套餐里 Access 的基础功能是够用的,官方显示免费版最多可以配置一定数量的用户和策略,个人使用完全不会触及上限。这一步让我安心很多:以前用 frp 还得自己在应用层做登录页,现在很多内网服务入口我都是靠 Access 统一保护。
5. 实操中的坑:日志、断流和多服务共存
5.1 日志怎么读,什么算正常
用 cloudflared 排查问题时,第一件事就是开日志:
正常运行时,日志里会出现大量 Registered tunnel connection、Connection registered 之类的信息,这说明隧道在 Cloudflare 边缘节点注册成功。如果出现 ERR 开头的行,重点看后面的错误码或错误串。常见的比如本地端口没监听,日志里会直接告诉你连接 localhost:8080 失败。
还有一点容易误解:日志显示 Connection 123456789 registered,不代表你的本地服务就是通的。它只说明隧道本身建立了,实际请求到达本地服务还有一层转发。所以排查时流程应该是:隧道日志有没有报错 -> 本地服务端口有没有监听 -> 访问域名时 Cloudflare 返回什么状态码。
5.2 断流和自动重启
最长遇到的问题就是隧道跑着跑着断流,外部访问超时。我遇到过的原因主要有几类:
第一是本地机器休眠或网络切换。家里 NAS 如果挂在 Wi-Fi 下,网络一抖动,出站连接就会断。解决办法就是前面提到的 systemd Restart=always,或者 Docker 的 --restart=unless-stopped,让进程自己拉起来。我后来直接把 NAS 改成有线连接,断流次数明显减少。
第二是 cloudflared 长时间运行后内存异常。这个在老版本里比较明显,更新到较新版本后改善很多。我的习惯是固定用官方 deb 包更新,跑一段时间就留意一下 systemctl status 里的内存占用,超过预期就直接重启。
第三是自动升级带来的意外。cloudflared 默认可能会自动升级,如果你用的是临时目录或特殊权限,升级失败会导致进程状态不稳定。我一般会在 systemd 的 ExecStart 里加上 --no-autoupdate,升级统一由自己控制,避免生产环境里半夜悄悄换版本。
5.3 多服务共用一条隧道的ingress规则
如果你只想跑一条隧道,又要暴露多个本地服务,ingress 规则的细节就很重要。规则顺序是自上而下匹配的,第一条命中的规则生效,所以要注意两条规则不要配成都能匹配同一个 hostname。
另外,service 字段里写的是本地服务地址,通常只写 http://localhost:端口,不要在 service 里加路径。你会发现即使写上路径也不会生效,因为 hostname 规则并不做路径转发。如果你需要“同域名不同路径转发到服务 A 和服务 B”,cloudflared 的 ingress 规则不适合这件事,更好的做法是在本地用一个 Nginx/Caddy 做路径分流,然后 cloudflared 只暴露这个反代服务的地址。
还有一个小坑是 HTTPS 本地服务。如果你的本地服务本身就是 HTTPS,配置 service: https://localhost:8443 时,cloudflared 默认会校验证书。自签名证书会导致握手失败,这时候可以给这条 ingress 规则加一个 noTLSVerify: true。但这样做会有中间人风险,我建议优先给本地服务换上受信任的证书,别图省事。
6. 收尾:两条关于日常使用的实在建议
6.1 把cloudflared当成系统服务来养
如果你只是临时演示,前台跑一条命令没问题。但一旦进入“常态化使用”,我非常建议把命名隧道做成系统服务。cloudflared 本身就支持 cloudflared service install,它会基于当前用户和配置安装一个后台服务。我的经验是:先用配置文件把 ingress 写清楚,再安装服务,最后用 systemctl enable --now cloudflared 开机自启。这样家里断电重启后,NAS 起来,隧道自动恢复,不需要每次手动登录和启动。免费内网穿透工具里,能做到这种“接近零维护”体验的不多。
6.2 对免费额度保持理性,别拿隧道扛大流量
最后想说一句实际体验:cloudflared 很香,但免费套餐终归是个人级服务。我在实际使用中主要把它用于 NAS 管理界面、API 调试、git 私有仓库和临时演示,这些场景流量小、请求频率低,跑起来相当稳定。但如果你的目标是给生产环境中的重要系统做公网入口,或者长期提供大流量下载,我建议还是认真评估一下付费套餐,或者直接换用更合适的基础设施方案。工具本身没有好坏,关键在于你知道它适合什么场景、边界在哪里。我自己的体会是:先想清楚要透传的服务性质,再决定用哪一层方案,远比盲目追求“免费”更重要。