背景

我在家里的内网中的若干台服务器上运行了一些服务,包括一些web服务,分别监听在不同端口上(如 80xx 的 HTTP 服务、84xx 的 HTTPS 服务)。对于其中的一个web服务,我希望通过一个域名子域名,用标准的 443 端口从公网访问这些服务,而且不需要在路由器上做端口映射或暴露家庭公网 IP。

方案选择:Cloudflare Tunnel

以下的配置,我使用我的一台 Mac mini 作为跳板机器。将它自身的服务以及内网的其他服务暴露出去。

Cloudflare Tunnel 是最佳方案,原因如下:

步骤一:在 Cloudflare 注册域名或者配置域名

在 Cloudflare Registrar 注册域名后,域名会自动使用 Cloudflare 的 DNS。这一步不是必须的。只要你拥有自己的域名,就可以通过以下两种方式实现同样的效果:

  1. 将域名托管到 Cloudflare;
  2. 自己设置域名record

步骤二:创建 Tunnel

  1. 登录 Cloudflare 控制台,进入 Networking > Tunnels
  2. 点击 Create a tunnel,输入名称(如 mac-mini)
  3. 选择 macOS 平台
  4. 安装方式选择 Install as service(推荐)
    • Install cloudflared:仅安装程序,需手动启动,适合临时测试
    • Install as service ✅:注册为系统服务(macOS launchd),开机自启动、崩溃自动重启
    • Run tunnel manually:直接在终端运行,关闭终端即断开,适合快速调试
  5. 复制安装命令备用

步骤三:在 Mac mini 上安装 cloudflared

如下

安装 cloudflared

brew install cloudflared

确认安装成功:

cloudflared --version

注册为系统服务

运行控制台提供的安装命令(包含你的 Tunnel token):

sudo cloudflared service install eyJhIjoi...<你的TOKEN>

⚠️ 安全提醒:Tunnel token 是你的连接凭证,任何持有它的人都可以连接你的 Tunnel。不要在公开场合分享。

步骤四:排查连接问题

安装后,如果控制台一直显示 "Waiting for your Tunnel to connect...",按以下步骤排查:

1. 手动启动服务

sudo launchctl start com.cloudflare.cloudflared

2. 查看日志

cat /Library/Logs/com.cloudflare.cloudflared.err.log

3. 常见问题:WARP 冲突

如果日志中出现以下错误:

ERR Failed to dial a quic connection error="failed to dial to edge with quic: timeout: no recent network activity"
ERR Unable to establish connection with Cloudflare edge error="TLS handshake with edge error: EOF"

并且连接的 IP 是 198.18.x.x(WARP 本地代理 IP 段),说明 Cloudflare WARP 客户端正在拦截 cloudflared 的流量。

原因:WARP 开启后会接管设备的所有出站流量,包括 cloudflared 需要的 UDP 7844 端口(QUIC 协议)连接,导致协议冲突。

解决方法:关闭 WARP

在 Mac mini 上点击菜单栏的 WARP 图标,将开关切换到关闭,然后重启 tunnel 服务:

sudo launchctl stop com.cloudflare.cloudflared
sudo launchctl start com.cloudflare.cloudflared

检查日志确认连接成功:

tail -20 /Library/Logs/com.cloudflare.cloudflared.err.log

看到 Registered tunnel connection 即表示连接成功。

关于 WARP:WARP 是 Cloudflare 的客户端 VPN,用于加密设备流量和隐藏 IP。对于作为服务器使用的 Mac mini,关闭 WARP 基本没有负面影响 — Tunnel 本身已经提供了加密和安全防护。WARP 更适合客户端设备(手机、笔记本)使用。

步骤五:配置 Public Hostname(端口映射)

Tunnel 连接成功后,配置路由规则将子域名映射到本地服务:

  1. 打开 Tunnel 的 Configure > Public Hostname 标签
  2. 点击 Add a public hostname
  3. 填写:
字段 值
Subdomain a(你想用的子域名)
Domain 选择你的域名(如 foobar.page)
Type HTTP(本地服务是 HTTP)或 HTTPS(本地服务有 SSL)
URL localhost:8080(本地服务地址和端口)
  1. 如果本地 HTTPS 服务用的是自签名证书,展开 Additional application settings > TLS > 开启 No TLS Verify
  2. 点击 Save hostname

Cloudflare 会自动创建 CNAME DNS 记录,将子域名指向 Tunnel。

关于 Type(HTTP / HTTPS)的说明

Type 指的是 cloudflared 连接本地服务时使用的协议,不是外部访问的协议。外部访问始终是 https://(443 端口加密),这是 Cloudflare 自动处理的。

本地服务情况 Type URL 说明
8080 端口,无 SSL HTTP localhost:8080 cloudflared 用 HTTP 连接本地服务
8443 端口,有 SSL HTTPS localhost:8443 cloudflared 用 HTTPS 连接本地服务

无论选 HTTP 还是 HTTPS,外部用户访问的都是 https://a.你的域名.com(443 端口,加密的)。

关于本地服务地址的选择

推荐使用 localhost:端口 而不是 公网域名:端口:

方式 优点 缺点
localhost:8080 ✅ 本地回环,最快最稳定,不依赖外部 DNS 无
my.original-domain-name.com:8443 可以复用已有域名 可能遇到 hairpin NAT 问题,增加延迟,多一个故障点

多端口映射

同一个 Tunnel 可以添加多个 Public Hostname,分别指向不同的本地服务端口:

Subdomain Domain Type URL
a foobar.page HTTP localhost:8089
b foobar.page HTTPS localhost:8443

最终效果

访问 https://a.foobar.page(443 端口)→ Cloudflare 网络 → Tunnel → http://localhost:8080(Mac mini 本地服务)

全程加密、无需公网 IP、无需端口映射、开机自启动。

参考链接