把 localhost:3000 绑定到自己的域名:Cloudflare Tunnel、ngrok、frp 与 Tailscale Funnel 对比
本地开发最常见的需求是:应用跑在 localhost:3000,但你希望外部的人、Webhook 平台、手机真机、客户预览环境或远程同事能通过一个公网 HTTPS 地址访问它。
如果只是临时调试,ngrok http 3000 足够快。如果你已经有自己的域名,并且希望长期用 dev.example.com 指到本机服务,Cloudflare Tunnel 通常是更稳的选择:不需要路由器端口转发,不需要本机有公网 IP,也不需要把家庭或办公室网络直接暴露到公网。
一句话选择
| 场景 | 推荐 | 特点 |
|---|---|---|
| 最快调试 | ngrok | 一条命令把 localhost:3000 暴露出去。免费版可以使用账号分配的 dev domain,但免费版不能选择或保留自己的自定义域名。 |
| 有自己的域名,想稳定用 | Cloudflare Tunnel | 最推荐。可以把 dev.example.com 指到本机 localhost:3000,不用开路由器端口,也不暴露公网 IP。Cloudflare 官方文档支持通过 public hostname 把公网域名路由到本地服务。 |
| 自建、可控、可放国内云服务器 | frp | 需要一台有公网 IP 的 VPS。支持 HTTP、HTTPS、TCP、UDP,可以用自己的域名转发到内网服务。 |
| 已有 Tailscale 网络 | Tailscale Funnel | 可以把本机服务暴露到公网 HTTPS,更偏 Tailscale 生态。官方仍标为 beta,并且公网域名主要是 tailnet-name.ts.net 体系。 |
我的默认建议:
- 只测一次 Webhook:用 ngrok。
- 想长期把
dev.example.com、preview.example.com绑定到本机:用 Cloudflare Tunnel。 - 想完全自管入口、日志、带宽、国内访问链路:用 frp 加一台 VPS。
- 团队已经在 Tailscale 里,且接受
*.ts.net域名和 beta 状态:用 Tailscale Funnel。
核心对比
| 维度 | ngrok | Cloudflare Tunnel | frp | Tailscale Funnel |
|---|---|---|---|---|
| 是否需要公网 VPS | 不需要 | 不需要 | 需要 | 不需要 |
| 是否支持自己的域名 | 支持,但自定义域名属于付费能力 | 支持,域名需要接入 Cloudflare | 支持,DNS 指到自己的 VPS | 不适合自定义域名,主要使用 tailnet 域名 |
| 免费临时域名 | 有账号分配的 dev domain | Quick Tunnel 可生成 trycloudflare.com 随机域名 | 取决于你的 VPS 和域名 | 使用 Tailscale Funnel URL |
| 适合长期稳定入口 | 中等,取决于套餐和域名能力 | 强 | 强,但要自己运维 | 中等,受生态和限制影响 |
| 典型协议 | HTTP、HTTPS、TCP、TLS | HTTP/HTTPS 公网应用最顺手;非 HTTP 服务通常需要客户端侧 cloudflared | HTTP、HTTPS、TCP、UDP | TLS 加密连接,官方限制端口为 443、8443、10000 |
| 运维复杂度 | 低 | 低到中 | 中到高 | 低到中 |
| 安全入口能力 | ngrok policy、basic auth、OAuth 等 | 可叠加 Cloudflare Access、WAF、DDoS 防护、DNS、证书 | 需要自己做认证、TLS、限流和审计 | 依赖 Tailscale ACL、Funnel node attribute 和 HTTPS |
| 最适合 | 临时 Demo、Webhook 调试 | 自有域名稳定预览、团队开发环境 | 自托管、国内 VPS、非 HTTP 端口转发 | 已有 Tailscale tailnet 的轻量公开分享 |
这里的关键差异不是“谁能穿透内网”,而是谁来持有公网入口:
公网入口越托管,越省事;越自建,越可控。
Cloudflare Tunnel 的工作方式
Cloudflare Tunnel 的核心组件是 cloudflared。它运行在你的机器上,主动向 Cloudflare 建立出站连接。公网用户访问 dev.example.com 时,请求先到 Cloudflare,再沿着这条隧道转发到本机 localhost:3000。
这和传统端口映射不一样:
| 传统端口映射 | Cloudflare Tunnel |
|---|---|
| 路由器或安全组开放公网端口 | 本机只需要能主动访问 Cloudflare |
| 公网能看到你的源站 IP | 源站 IP 不需要公开 |
| HTTPS、证书、DDoS、WAF 要自己处理 | 可以复用 Cloudflare 的边缘能力 |
| 家庭宽带、公司 NAT、动态 IP 容易麻烦 | 对 NAT 和动态 IP 更友好 |
前提条件:
- 有 Cloudflare 账号。
- 你的域名已经接入 Cloudflare DNS。
- 本机或服务器可以访问外网,并能运行
cloudflared。 - 你的本地应用已经在
http://localhost:3000正常工作。
安装 cloudflared
如果你通过 Cloudflare Dashboard 创建 Tunnel,页面会根据系统生成安装和运行命令,直接复制执行即可。这是最省心的路径。
手动安装可以按系统选择:
# macOS
brew install cloudflared
# Debian / Ubuntu
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt-get update && sudo apt-get install cloudflared
# RHEL / CentOS / Fedora 系
curl -fsSl https://pkg.cloudflare.com/cloudflared.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
sudo yum update && sudo yum install cloudflared
确认安装:
cloudflared --version
Docker 也可以直接运行:
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token <TUNNEL_TOKEN>
最快试用:Quick Tunnel
如果只是临时看一下效果,不需要账号和自定义域名,可以先跑 Quick Tunnel:
# 先启动你的本地项目
npm run start
# 另开一个终端
cloudflared tunnel --url http://localhost:3000
它会生成一个随机的 trycloudflare.com 子域名。这个模式适合临时测试,不适合长期入口:Cloudflare 官方文档说明 Quick Tunnel 面向测试,并且有并发请求和部分能力限制。
只要你想使用自己的 dev.example.com,就应该创建正式 Tunnel。
方式一:Dashboard 创建 Tunnel
这是推荐给大多数人的方式,因为 token、服务安装命令、Tunnel 健康状态都在页面里。
1. 启动本地应用
以 Docusaurus 或普通前端项目为例:
npm run start
确认本机可以访问:
curl -I http://localhost:3000
2. 创建 Tunnel
进入 Cloudflare Dashboard:
- 打开
Networking->Tunnels。 - 选择
Create Tunnel。 - 输入名称,比如
rainlib-dev。 - 选择运行
cloudflared的操作系统和 CPU 架构。 - 复制页面给出的 install/run 命令,在本机执行。
- 等待 Tunnel 状态变成
Healthy。
Dashboard 模式通常会给出类似下面的服务安装命令:
sudo cloudflared service install <TUNNEL_TOKEN>
这个 token 是敏感凭证,不要提交到 Git,也不要发到聊天群。
3. 添加公网 Hostname
在 Tunnel 详情页的 Routes 中添加 route:
| 字段 | 示例 |
|---|---|
| Type | Published application |
| Subdomain | dev |
| Domain | example.com |
| Service URL | http://localhost:3000 |
保存后,Cloudflare 会把 https://dev.example.com 路由到本机服务。浏览器打开:
https://dev.example.com
如果你的应用在另一台内网机器上,也可以把 Service URL 写成:
http://192.168.1.20:3000
前提是运行 cloudflared 的机器能访问这个内网地址。
方式二:CLI 本地配置 Tunnel
如果你希望配置文件可审计、可备份,或者不想把所有规则都放在 Dashboard 里,可以使用 locally-managed tunnel。
1. 登录 Cloudflare
cloudflared tunnel login
命令会打开浏览器,让你登录 Cloudflare 并选择域名。完成后,本机会生成 cert.pem。
2. 创建命名 Tunnel
cloudflared tunnel create rainlib-dev
cloudflared tunnel list
创建成功后会得到 Tunnel UUID,并在 ~/.cloudflared/ 下生成凭证文件。
3. 写配置文件
创建 ~/.cloudflared/config.yml:
tunnel: <Tunnel-UUID>
credentials-file: /Users/<you>/.cloudflared/<Tunnel-UUID>.json
ingress:
- hostname: dev.example.com
service: http://localhost:3000
- service: http_status:404
Linux 上路径通常是:
credentials-file: /home/<you>/.cloudflared/<Tunnel-UUID>.json
注意最后一条 catch-all 规则。Cloudflare 文档要求包含兜底 ingress 规则,否则未匹配的请求不知道该如何处理。这里用 http_status:404 是最干净的默认值。
4. 创建 DNS 路由
cloudflared tunnel route dns rainlib-dev dev.example.com
这会创建一个 CNAME,指向类似 <Tunnel-UUID>.cfargotunnel.com 的目标。
5. 前台运行测试
cloudflared tunnel run rainlib-dev
浏览器访问:
https://dev.example.com
6. 服务化运行
macOS 登录后自动运行:
cloudflared service install
macOS 开机运行:
sudo cloudflared service install
sudo launchctl start com.cloudflare.cloudflared
Linux systemd:
cloudflared service install
systemctl start cloudflared
systemctl status cloudflared
如果你用 sudo 安装 Linux service,要注意 $HOME 可能变成 /root。配置文件在普通用户目录时,应显式指定:
sudo cloudflared --config /home/<USER>/.cloudflared/config.yml service install
sudo systemctl restart cloudflared
Cloudflare Tunnel 常见配置
多个子域名
tunnel: <Tunnel-UUID>
credentials-file: /Users/<you>/.cloudflared/<Tunnel-UUID>.json
ingress:
- hostname: dev.example.com
service: http://localhost:3000
- hostname: api-dev.example.com
service: http://localhost:8080
- hostname: admin-dev.example.com
service: http://localhost:9000
- service: http_status:404
匹配路径
ingress:
- hostname: dev.example.com
path: /api/*
service: http://localhost:8080
- hostname: dev.example.com
service: http://localhost:3000
- service: http_status:404
转发到局域网服务
ingress:
- hostname: nas.example.com
service: http://192.168.1.50:5000
- service: http_status:404
不要把 NAS、数据库、管理后台裸露到公网。至少叠加 Cloudflare Access、强认证、IP 限制或应用自身登录。
安全建议
Cloudflare Tunnel 解决的是“公网入口如何到达本机”,不是“你的应用是否应该被任何人访问”。
建议最少做这些事:
- 使用独立子域名,比如
dev.example.com,不要直接占用主站根域名。 - 本机服务保持监听
127.0.0.1即可,cloudflared在同一台机器时不需要应用监听0.0.0.0。 - 不要把 Tunnel token、
cert.pem、credentials JSON 提交到仓库。 - 预览站、后台、API 文档、调试面板建议加 Cloudflare Access。
- 如果只是给 Webhook 平台调用,优先限制路径、加签名校验、检查来源。
- 对长期运行的 Tunnel 配置监控和重启策略,避免电脑睡眠后入口不可用。
排障清单
| 现象 | 排查方向 |
|---|---|
502 Bad Gateway | 本地服务没有启动,端口写错,或者 cloudflared 机器访问不到 localhost:3000。 |
| Tunnel 不健康 | 检查 cloudflared 是否在运行,网络是否能访问 Cloudflare,受限网络下留意出站端口 7844。 |
| 域名打不开 | 检查域名是否已接入 Cloudflare,CNAME 是否创建,Tunnel route 是否绑定到了正确 hostname。 |
| 本地能访问,公网访问样式或接口异常 | 检查应用里的 baseUrl、CORS、allowed hosts、回调地址和 cookie domain。 |
| HMR 或 WebSocket 异常 | 开发服务器可能需要允许外部 host,或者需要检查 HTTPS、WebSocket 地址和代理头。 |
| service 启动后配置不生效 | 重启 cloudflared service,并确认 service 读取的是你编辑的那个 config.yml。 |
ngrok 怎么用
ngrok 最适合“马上给一个公网地址”:
ngrok config add-authtoken <NGROK_AUTHTOKEN>
ngrok http 3000
如果你有可用的 ngrok 域名:
ngrok http 3000 --url https://example.ngrok.app
ngrok 的优点是极快、跨平台、调试体验好,适合 Webhook、本地 Demo、短时间联调。限制是免费版域名能力有限:官方 Domains 文档说明,免费账号可以使用自动分配的 dev domain;选择或保留自定义域名需要付费计划,自带域名属于更高套餐能力。
frp 怎么配置
frp 的模式是你自己准备一台有公网 IP 的 VPS,公网入口由 frps 持有,本机运行 frpc 主动连出去。
VPS 上的 frps.toml:
bindPort = 7000
vhostHTTPPort = 80
vhostHTTPSPort = 443
启动:
./frps -c ./frps.toml
本机 frpc.toml:
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "web"
type = "http"
localIP = "127.0.0.1"
localPort = 3000
customDomains = ["dev.example.com"]
启动:
./frpc -c ./frpc.toml
DNS 配置:
dev.example.com A x.x.x.x
frp 的优点是自托管、协议支持广、成本可控,适合你已经有云服务器、希望走国内线路、需要 TCP/UDP 或更多代理能力的场景。代价是你要自己处理 VPS 安全、系统更新、TLS 证书、日志、限流、鉴权和进程守护。
Tailscale Funnel 怎么看
Tailscale Funnel 适合已经使用 Tailscale 的团队:你可以把本机服务通过公网 HTTPS 暴露出去,让不在 tailnet 里的用户也能访问。
典型命令形态:
tailscale serve --bg 3000
tailscale funnel 443 on
它的边界也很明确:
- 官方文档仍标为 beta。
- 需要 Tailscale 版本、MagicDNS、HTTPS 证书和 tailnet policy 中的 Funnel node attribute。
- Funnel 只能使用 tailnet 的 DNS 名称,比如
machine.tailnet-name.ts.net。 - 官方限制监听端口为
443、8443、10000。 - 更适合 Tailscale 生态里的分享,不是“把任意自有域名稳定绑定到 localhost”的首选。
如果你的诉求是 dev.example.com -> localhost:3000,Cloudflare Tunnel 更直接。
最终建议
| 你想要什么 | 直接选 |
|---|---|
| 今天马上给别人看本地页面 | ngrok |
| 把自己的域名长期指向本机开发服务 | Cloudflare Tunnel |
| 有 VPS,想完全自管入口和转发规则 | frp |
团队已经在 Tailscale,并接受 beta 和 ts.net 域名 | Tailscale Funnel |
对个人开发者和小团队来说,Cloudflare Tunnel 是最均衡的方案:配置比 frp 少,域名能力比 ngrok 免费版稳定,安全能力又比简单端口转发完整。它尤其适合把 dev.example.com、preview.example.com、webhook-dev.example.com 这类开发入口长期挂到本机或内网机器上。