跳过正文

无公网?依旧开放服务!

·5 分钟· loading
asm2apex
作者
asm2apex
目录

博文概览
#

博文概览

你有没有这种场景:家里/公司内网跑了个服务,想从外面访问,但公网 IP 没有,路由器还在装死,运营商还顺手给你一个大内网。

这时候你要么去折腾端口映射、动态域名、各种“看起来很专业但一碰就碎”的链路;要么你承认现实:把“公网入口”交给 Cloudflare,内网只负责出站连接。

这篇文章讲两件事:

  1. 在 Cloudflare 最新的 Zero Trust 控制台里创建 Tunnel(隧道),安装 connector(连接器),把内网服务挂出去
  2. 用 Vercel 做一层反向代理,把访问入口放到 Vercel 这边,试图优化国内访问体验

先说清楚一件事:第二步是否“变快”,取决于你的网络路由现实情况。这个不确定性来自网络环境本身,不来自你写的配置。最保守的做法是:做完后用实际测速(TTFB、丢包)验证,别靠想象力。

0. 你需要准备什么
#

  • 一个 Cloudflare 账号,并且能打开 Cloudflare Zero Trust(以前叫 Cloudflare Access/Teams)
  • 一个域名(建议你自己的域名,别长期用 trycloudflare.com 那种临时域名)
  • 内网里能跑 cloudflared 的一台机器(NAS、软路由、Linux 服务器、Mac mini 都行)
  • 一个要暴露的服务(示例用 HTTP 服务:http://192.168.1.10:8080
  • 一个 Vercel 账号(用于反向代理)

1. 开启 Zero Trust:入口在哪
#

Cloudflare 的页面经常改名字,但大体路径是:

Cloudflare Dashboard -> Zero Trust

如果你是第一次进 Zero Trust,会让你选团队名(Team name)并初始化。按提示走就行,别想太多,后面都能改。

2. 创建 Tunnel:让内网“自己拨出去”
#

2.1 在 Zero Trust 里创建 Tunnel
#

路径通常是:

Zero Trust -> Networks -> Tunnels -> Create a tunnel

选择 connector 类型:cloudflared
给 tunnel 起个名字,比如 home-lab,然后下一步会给你一个 token(长得很丑但很重要)。

2.2 在内网机器安装 cloudflared
#

只给一种:Docker Compose。原因也很简单:可迁移、可复用、机器重启自动拉起,最不容易“人走服务死”。

新建 docker-compose.yml

services:
    cloudflared:
      image: cloudflare/cloudflared:2024.11.1
      container_name: cloudflared
      restart: unless-stopped
      command: tunnel --protocol=http2 --no-autoupdate run --token ${CLOUDFLARED_TOKEN}

然后在同目录准备 .env(别把 token 直接写进 yml):

CLOUDFLARED_TOKEN=xxxx

启动:

docker compose up -d
docker compose logs -f cloudflared

2.3 启动 connector(关键是 token)
#

上面 docker compose up -d 跑起来后,这一步你就已经完成了:connector 已经在容器里常驻运行。

到这里为止,你做了一件很重要的事:内网机器只需要“出站连接”,不需要开任何入站端口。NAT、运营商、路由器这些麻烦事,被你绕过去了。

3. 把内网服务挂到公网域名
#

3.1 添加 Public Hostname(把域名和内网服务绑定)
#

回到 Zero Trust 的 tunnel 页面,找到你刚创建的 tunnel,进入配置:

Public Hostnames -> Add a public hostname

你需要填三样东西:

  • Subdomain:例如 cf-site
  • Domain:例如 your-domain.com
  • Service:例如 http://192.168.1.10:8080

保存后,Cloudflare 会在你的 DNS 里加一条记录(本质是 CNAME 到 xxxx.cfargotunnel.com)。这条记录就是外界访问入口。

3.2(可选但推荐)用 Access 给它加门禁
#

如果你不想让全世界都来敲你的门(这是正常人的选择),就用 Access:

Zero Trust -> Access -> Applications -> Add an application -> Self-hosted

填你的域名(比如 cf-site.your-domain.com),然后配置 policy(策略),例如:

  • 只允许你自己的邮箱域名登录
  • 只允许某个 GitHub 组织成员

这一步做完,你的服务就不是“暴露在公网”,而是“放在门禁后面”。这才符合 Zero Trust 的名字,不然只是换个姿势裸奔。

4. 现实问题:国内访问 tunnel 域名不稳定/偏慢
#

这类问题通常不是你服务慢,是链路绕、抖、丢包。你会看到:

  • 首次连接慢(TTFB 大)
  • 偶尔加载半天
  • 有些地区直接抽风

所以你才想“加一层 Vercel 反代”。可以,但别神化它:它只是换了入口网络,效果靠实测说话。

5. 用 Vercel 做反向代理:把入口搬到另一张网
#

5.1 关键前提:别把入口又放回 Cloudflare 代理
#

你想让用户先到 Vercel,就别让用户的域名再被 Cloudflare 的橙云代理一次。否则你绕了一圈又回去了。

最简单的做法:

  • 用一个新子域名给 Vercel(例如 vercel-site.your-domain.com
  • DNS 记录指向 Vercel(按 Vercel 的提示添加 CNAME/A 记录)
  • 这条 DNS 记录用 DNS only(灰云),不要走 Cloudflare 代理

5.2 最小可用方案:用 Vercel Rewrite 直接代理到 tunnel 域名
#

新建一个最小项目(随便一个静态项目都行),在仓库根目录加 vercel.json

{
  "version": 2,
  "routes": [
    {
      "src": "/(.*)",
      "dest": "https://cf-site.your-domain.com"
    }
  ]
}

然后把 vercel-site.your-domain.com 绑定到这个 Vercel 项目。访问:

  • 用户访问:https://vercel-site.your-domain.com/
  • Vercel 在边缘节点帮你转发到:https://cf-site.your-domain.com/

这就是反向代理。

5.3 你会踩的坑(提前告诉你,省得你骂浏览器)
#

  1. Host/Origin 相关校验
    有些应用会校验 Host 或做绝对跳转,导致跳来跳去。最保守的处理是:先用纯静态或简单服务验证链路,再处理应用层的问题。

  2. WebSocket/长连接
    部分长连接在这种多层代理下会出问题。你如果有这类需求,别靠“配置碰碰运气”,要专门测试。

  3. Cloudflare Access 的登录跳转
    如果你给 tunnel 域名加了 Access 门禁,Vercel 反代会遇到登录和 Cookie 的问题。最稳的做法是:

  • Vercel 用作“加速入口”的域名,就不要再套一层 Access 登录
  • 或者你接受它不透明,需要额外适配(这不是三行配置能解决的事)

6. 验证:别靠感觉,拿数据说话
#

你至少要测三样:

  • 直接访问 tunnel 域名:https://cf-site.your-domain.com/
  • 访问 Vercel 代理域名:https://vercel-site.your-domain.com/
  • 对比 TTFB、整体加载时间、丢包/超时情况

Chrome DevTools 的 Network 面板就够用:看首字节时间、看重试、看请求是不是在“卡死”。

7. 我自己的结论(到目前为止)
#

Cloudflare Tunnel 解决的是“无公网怎么暴露服务”的问题,属于基础设施级别的可靠方案:内网只出站,不折腾端口映射,稳定性更可控。

Vercel 反代解决的是“入口网络换一换”的问题:它不保证变快,但给了你一个可对比的实验入口。做工程就该这样,先把变量隔离出来,再用数据验证。