以前想让外网访问家里的服务,路子无非两条:要么在路由器上做端口映射并暴露公网 IP,要么租一台有公网 IP 的 VPS 做反向代理。

这两种做法都有个共同的毛病——你确实在公网上开了一个入口。哪怕只转发 443,扫描器也会很快找上门。

Cloudflare Tunnel 换了思路:连接是从内网主动向外建立的,公网侧根本不存在可以扫描的入站端口。

原理

1
访客 → Cloudflare 边缘节点 →(已建立的长连接)→ cloudflared → localhost:3000

cloudflared 进程在你这台机器上跑着,启动时主动连到 Cloudflare 并保持一条隧道。外部请求到达边缘节点后,顺着这条已建立的连接回传进来。

所以:

  • 路由器上不需要开任何端口
  • 不需要公网 IP,NAT 后面就行
  • 出口流量,防火墙通常不会拦

基本用法

1
2
3
cloudflared tunnel login
cloudflared tunnel create my-blog
cloudflared tunnel route dns my-blog blog.example.com

配置文件 ~/.cloudflared/config.yml:

1
2
3
4
5
6
7
8
9
tunnel: ff19965a-1b58-45e6-91bc-6d1d2202293c
credentials-file: /home/user/.cloudflared/ff19965a-....json

ingress:
- hostname: blog.example.com
service: http://localhost:3000

# 兜底规则必须放最后,否则 cloudflared 会拒绝启动
- service: http_status:404

最后那个 http_status:404 是必需的——少了它 cloudflared 会启动失败,因为 ingress 规则要求有一个不带 hostname 的兜底项。

几个会踩的点

DNS 记录必须指向隧道。 用 tunnel route dns 加出来的记录是一条 CNAME,指向 <隧道ID>.cfargotunnel.com,而且必须保持**橙色云朵(已代理)**打开。手动改成 DNS only 就废了。

Windows 上装成服务:

1
cloudflared service install

区分「隧道」和「DNS」两套权限。 Cloudflare 的 API Token 权限粒度很细——能改 DNS 的 Token 未必能管理隧道,Account → Cloudflare Tunnel → Edit 是另一个独立权限。报 9109 的时候先怀疑这个。

和 Pages 怎么选

Cloudflare Tunnel Cloudflare Pages
需要常开的机器 是 否
适合动态服务 是 否(纯静态)
全球加速 依赖源站 原生 CDN
运维成本 中 几乎为零

结论很简单:静态博客用 Pages,动态服务才上 Tunnel。 让一台机器整年开着只为托管几页 Markdown,实在没必要。

不过 Tunnel 有个 Pages 替代不了的优势:本地开发环境可以挂个域名直接给别人看,改完代码刷新即生效,不用重新部署。