Share

外观
风格

Cloudflare Tunnel:不开端口也能访问家里的服务

2026年2月8日 · 专栏

封面图

Cloudflare Tunnel:不开端口也能访问家里的服务

想在外面访问家里 NAS 上的服务,但:

  • 没有公网 IP(运营商给的是 NAT)
  • 不想在路由器开端口(安全隐患)
  • 不想用 ngrok(免费版域名随机、有限制)

后来发现 Cloudflare Tunnel:在本地装个 cloudflared,它主动连到 Cloudflare 边缘节点,外面通过域名访问时请求自动转发到本地。不用开端口,自带 HTTPS,还能加登录保护。

现在我的任务管理器 Web UI 就这么暴露的:https://mt.example.com,出门也能看。


这是什么

Cloudflare Tunnel(原名 Argo Tunnel):

  • 本地运行 cloudflared,它主动连接 Cloudflare
  • 外部请求通过 Cloudflare 边缘节点 → Tunnel → 本地服务
  • 不需要公网 IP,不需要开端口
  • 自动 HTTPS(Cloudflare 证书)
  • 可选登录保护(Cloudflare Access)

架构图:

用户浏览器
    │
    ▼
Cloudflare 边缘节点
    │ (加密)
    ▼
cloudflared (你的电脑)
    │
    ▼
本地服务 (localhost:5700)

效果展示

# 本地服务跑在 5700 端口
mt serve  # 任务管理器 Web UI

# 通过公网域名访问
curl https://mt.example.com
# 正常返回页面!

外面用手机也能访问,还能加 Google 登录保护。


快速开始

1. 安装 cloudflared

# macOS
brew install cloudflared

# Linux
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared
chmod +x cloudflared && sudo mv cloudflared /usr/local/bin/

# 验证
cloudflared --version

2. 登录 Cloudflare

cloudflared tunnel login

会打开浏览器,选择要使用的域名(必须已托管在 Cloudflare)。授权后会在 ~/.cloudflared/ 生成证书。

3. 创建 Tunnel

cloudflared tunnel create my-tunnel

会输出 Tunnel ID,类似 74db0e95-1ff7-4dd8-b0ec-87a776894010

4. 配置 ingress

创建 ~/.cloudflared/config.yml

tunnel: <TUNNEL_ID>
credentials-file: /Users/你的用户名/.cloudflared/74db0e95-xxx.json

ingress:
  - hostname: mt.example.com
    service: http://127.0.0.1:5700

  - service: http_status:404

ingress 是路由规则:

  • 访问 mt.example.com → 转发到本地 5700 端口
  • 其他请求 → 返回 404

5. 添加 DNS 路由

cloudflared tunnel route dns my-tunnel mt.example.com

这会在 Cloudflare DNS 自动创建一条 CNAME 记录。

6. 启动 Tunnel

cloudflared tunnel run my-tunnel

现在访问 https://mt.example.com 就能看到本地服务了。


开机自启(macOS)

# 安装为 LaunchAgent
cloudflared service install

# 但默认配置可能不对,建议手动创建 plist

手动创建 ~/Library/LaunchAgents/com.cloudflare.cloudflared.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.cloudflare.cloudflared</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/cloudflared</string>
        <string>--config</string>
        <string>/Users/你的用户名/.cloudflared/config.yml</string>
        <string>tunnel</string>
        <string>run</string>
        <string>my-tunnel</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardErrorPath</key>
    <string>/Users/你的用户名/Library/Logs/cloudflared.log</string>
</dict>
</plist>

加载:

launchctl load ~/Library/LaunchAgents/com.cloudflare.cloudflared.plist

添加登录保护(Cloudflare Access)

Tunnel 只负责网络连通,登录验证要用 Cloudflare Zero Trust 的 Access。

1. 进入 Zero Trust Dashboard

https://one.dash.cloudflare.com → Access → Applications

2. 创建 Application

  • Type: Self-hosted
  • Application name: mt
  • Session duration: 24 hours
  • Application domain: mt.example.com

3. 配置策略

  • Policy name: Allow me
  • Action: Allow
  • Include: Emails → 你的邮箱

保存后,访问 mt.example.com 会先跳转到 Cloudflare 登录页,验证邮箱后才能访问。


常用命令速查

# === Tunnel 管理 ===
cloudflared tunnel list              # 列出所有 tunnel
cloudflared tunnel info my-tunnel    # 查看 tunnel 状态
cloudflared tunnel run my-tunnel     # 启动 tunnel
cloudflared tunnel delete my-tunnel  # 删除 tunnel(谨慎!)

# === DNS 路由 ===
cloudflared tunnel route dns my-tunnel api.example.com  # 添加路由

# === macOS 服务管理 ===
launchctl list | grep cloudflared                        # 查看状态
launchctl kickstart -k gui/$(id -u)/com.cloudflare.cloudflared  # 重启
launchctl unload ~/Library/LaunchAgents/com.cloudflare.cloudflared.plist  # 停止

# === 调试 ===
tail -f ~/Library/Logs/cloudflared.log  # 查看日志

添加更多服务

一个 Tunnel 可以暴露多个服务,只需要修改 config.yml

ingress:
  - hostname: mt.example.com
    service: http://127.0.0.1:5700

  - hostname: api.example.com
    service: http://127.0.0.1:8787

  - hostname: jupyter.example.com
    service: http://127.0.0.1:8888

  - service: http_status:404

然后添加 DNS 路由并重启:

cloudflared tunnel route dns my-tunnel api.example.com
cloudflared tunnel route dns my-tunnel jupyter.example.com
launchctl kickstart -k gui/$(id -u)/com.cloudflare.cloudflared

我踩过的坑

1. cloudflared service install 配置不对

默认安装的 LaunchAgent 可能没有指定 config 文件和 tunnel 名。建议手动写 plist。

2. 忘记最后的 404 fallback

ingress 最后一条必须是 catch-all,否则 cloudflared 启动会报错:

- service: http_status:404

3. DNS 路由和 ingress 要匹配

如果 DNS 路由了 api.example.com,但 config.yml 里没有对应的 hostname,访问会 502。


对比其他方案

方案 优点 缺点
Cloudflare Tunnel 免费、自带 HTTPS、可加 Access 需要域名托管在 CF
ngrok 简单快速 免费版域名随机、有限制
frp 自部署可控 需要公网服务器
Tailscale 点对点、安全 需要两端都装客户端

Cloudflare Tunnel 适合:域名在 CF、想要公开访问、需要登录保护。


附录:给 AI 的复现指令

帮我用 Cloudflare Tunnel 把本地服务暴露到公网。

目标:本地 5700 端口的 Web 服务,通过 mt.example.com 访问。

前提:
- 域名 example.com 已托管在 Cloudflare
- 已安装 cloudflared

步骤:
1. cloudflared tunnel login(浏览器授权)
2. cloudflared tunnel create my-tunnel
3. 创建 ~/.cloudflared/config.yml:
   tunnel: <TUNNEL_ID>
   credentials-file: ~/.cloudflared/<TUNNEL_ID>.json
   ingress:
     - hostname: mt.example.com
       service: http://127.0.0.1:5700
     - service: http_status:404
4. cloudflared tunnel route dns my-tunnel mt.example.com
5. cloudflared tunnel run my-tunnel

开机自启(macOS):
- 创建 ~/Library/LaunchAgents/com.cloudflare.cloudflared.plist
- launchctl load 加载

可选登录保护:
- Cloudflare Zero Trust → Access → 创建 Self-hosted Application
- 配置 Email 策略允许指定邮箱

成功标志:浏览器访问 https://mt.example.com 能看到本地服务