问题所在
我家 ISP 的路由器有一个 Web 管理界面,可以配置防火墙规则和其他设置,但端口转发只对商业套餐开放。不管怎么 Google、怎么打客服电话,都改变不了这一点。没有端口转发,我笔记本上跑的任何东西,都无法从家庭网络外部访问。
作为一个开发者,我在工作中一直用 Microsoft Dev Tunnels —— 开发期间给本地服务提供临时公网 URL。我需要的是同样的东西,但要永久可用:自定义域名、以守护进程方式运行、而且免费。于是,我找到了 Cloudflare Tunnels。
什么是 Cloudflare Tunnel?
Cloudflare Tunnel 会从你的服务器到 Cloudflare 边缘网络建立一条仅出站的连接。流量走向是这样的:
用户 → Cloudflare 边缘 → 隧道 → 你的服务器
你的服务器主动拨出到 Cloudflare —— 不需要任何入站端口。NAT 和端口转发都不再重要,只要服务器能向 Cloudflare 发出站连接就行(严格防火墙需要放行到 Cloudflare 的出站访问,包括隧道运行所需的 7844 端口)。只要你的机器能发 HTTPS 请求,它就能对外提供服务。
这实际上比传统端口转发更安全,因为:
- 路由器上不暴露任何端口
- 不需要静态 IP
- Cloudflare 负责 DDoS 防护、SSL/TLS 和缓存
- 流量在传输中加密(注意:Cloudflare 在其边缘终止 TLS,因此它能以明文看到你的流量 —— 这个取舍后面再细说)
前置条件
- 一个托管在 Cloudflare 的域名(免费方案即可)
- 一个启用了 Zero Trust 的 Cloudflare 账号(最多 50 用户也是免费)
- 服务器上运行
cloudflared(Docker 或原生安装)
开始配置
1. 创建隧道
进入 Cloudflare Zero Trust 控制台 → Networks → Tunnels → Create a tunnel。
给它起个名字(比如 homeserver),Cloudflare 会生成一个隧道 token。这个 token 就是你的服务器用来认证的凭证。

2. 运行 cloudflared
最简单的方式 —— 一个 Docker 容器:
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: always
command: tunnel run
environment:
- TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
networks:
- homeserver
在 .env 文件里设置你的 token:
CLOUDFLARE_TUNNEL_TOKEN=eyJhIjoiYWJjZGVm...
就这样。docker compose up -d,隧道就上线了。
3. 添加公网主机名
回到 Cloudflare 控制台,为每个服务添加路由。每条路由把一个子域名映射到一个内部服务:
| 公网主机名 | 服务 | URL |
|---|---|---|
landing.yourdomain.com | HTTP | http://homepage:3000 |
books.yourdomain.com | HTTP | http://kavita:5000 |
ai.yourdomain.com | HTTP | http://open-webui:8080 |
或者,你也可以把一个泛域名子域指向一个反向代理(比如 Nginx),由它根据主机名做路由。我就是这么做的 —— 所有流量都进 cloudflared,再转发给 Nginx,Nginx 路由到正确的容器。这部分后面再讲。

架构总览
完整流程是这样的:
互联网
│
▼
Cloudflare Edge(SSL 终止、DDoS 防护)
│
▼(加密隧道,仅出站)
cloudflared 容器
│
▼
reverse-proxy(Nginx,端口 8000)
│
├── landing.* → homepage:3000
├── books.* → kavita:5000
├── ai.* → open-webui:8080
├── pdf.* → stirling-pdf:8080
└── ...(12+ 个服务)
所有容器都在同一个 Docker 网络(homeserver)里,所以 Nginx 可以通过容器名访问它们。
访问策略(可选但推荐)
对于没有自带认证的服务(比如我的主页仪表盘和开发工具),我用 Cloudflare Access 策略。这会在隧道到达你服务器之前,先加一道登录关卡:
- 进入 Zero Trust → Access → Applications
- 添加一个 Self-hosted application
- 设置域名(比如
landing.yourdomain.com) - 添加策略:允许匹配你自己邮箱的邮件,一次性 PIN 验证
这样一来,即使有人猜到了你的子域名,也会在触达任何东西之前先撞上 Cloudflare 的登录界面。


成本
- Cloudflare 账号:免费
- 隧道:免费(不计量带宽,不过 Cloudflare 免费方案条款不鼓励大量视频/文件下载)
- 域名:约 10 美元/年
- Zero Trust(最多 50 用户):免费
总计:一个域名的钱。
为什么不用 Tailscale?
这个问题经常被提出来。Tailscale 是另一个不用端口转发就能访问家庭服务的流行方案。Tailscale 在你的设备之间建立一个私有的、基于 WireGuard 的网状网络,所以你不需要公开暴露服务,也不需要跑公网反向代理。
Tailscale 很适合这些情况:
- 你主要需要从自己的设备访问(设备间的网状 VPN)
- 你想要一个配置最少的私有网络
- 你不需要自定义域名(Tailscale Funnel 给你的是
*.ts.netURL,不是你自己的域名,而且只限于443、8443、10000端口,仅 TLS,带宽限制不可配置)
我选 Cloudflare Tunnels 是因为:
- 我想要带自定义子域名的公网 URL —— 分享我的 Kavita 书库链接,或者让别人不用装任何东西就能访问某个服务
- Cloudflare 免费负责 SSL、DDoS 防护和缓存
- Zero Trust 访问策略让我能把特定服务挡在基于邮箱的登录后面(访问设备上不需要装 VPN 客户端)
- 它和我其他一切在用的同一个域名配合
取舍很清楚:Tailscale 更简单、更私密;Cloudflare Tunnels 给你带精细访问控制的公网服务。如果你不需要公网访问,Tailscale 是更省事的答案。我同时需要公网和私有两种访问模式,所以 Cloudflare 更合适。
你也可以两个都用 —— Tailscale 做管理访问(SSH、MeshCentral),Cloudflare 做面向用户的服务。很多家庭实验室玩家 apparently 就是这么干的。
前后对比
用 Cloudflare Tunnel 之前:
- 服务只能在家庭 WiFi 里访问
之后:
- 每个服务都能从任何地方用干净的 URL 访问
- SSL/TLS 自动处理
- 不暴露端口,不需要静态 IP
- 暴露一个新服务只要 30 秒
接下来
在我实际的折腾过程中,接下来配置的是 MeshCentral 用于远程访问(因为当时我每次还得插显示器)。但在这个系列里,我会按逻辑顺序来 —— 一层一层搭建基础设施,让你不用经历我做过的那些重构和调整。



暂无评论内容