博文概览 #

你有没有这种场景:家里/公司内网跑了个服务,想从外面访问,但公网 IP 没有,路由器还在装死,运营商还顺手给你一个大内网。
这时候你要么去折腾端口映射、动态域名、各种“看起来很专业但一碰就碎”的链路;要么你承认现实:把“公网入口”交给 Cloudflare,内网只负责出站连接。
这篇文章讲两件事:
- 在 Cloudflare 最新的 Zero Trust 控制台里创建 Tunnel(隧道),安装 connector(连接器),把内网服务挂出去
- 用 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 cloudflared2.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 你会踩的坑(提前告诉你,省得你骂浏览器) #
-
Host/Origin 相关校验
有些应用会校验 Host 或做绝对跳转,导致跳来跳去。最保守的处理是:先用纯静态或简单服务验证链路,再处理应用层的问题。 -
WebSocket/长连接
部分长连接在这种多层代理下会出问题。你如果有这类需求,别靠“配置碰碰运气”,要专门测试。 -
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 反代解决的是“入口网络换一换”的问题:它不保证变快,但给了你一个可对比的实验入口。做工程就该这样,先把变量隔离出来,再用数据验证。