局域网服务器要暴露到公网,传统做法是路由器端口映射、frp 或者 DDNS,但这几个方案都有硬伤:要开入站端口(NAT 暴露、公网扫描、被爆破风险高),还依赖公网 IP。这次用 Cloudflare Tunnel 做了一套「零入站端口」的内网穿透,记录一下关键操作和坑。
一、为什么选 Cloudflare Tunnel
- 不开入站端口:cloudflared 主动向外发起连接,公网根本扫不到你的服务器,隐蔽性极好;
- 免费:免费套餐就够个人用,支持 HTTP/TCP/SSH;
- 自带 HTTPS + CDN:CF 代理自动给子域名签证书,源站 IP 隐藏;
- 配置即代码:隧道配置可以在云端(Dashboard)或本地 config 维护,可批量加域名。
二、架构
公网用户 → https://napcat1.zain-dev.top
↓ Cloudflare 边缘 (proxy, TLS)
cloudflared 进程(本机出站连接)
↓ 127.0.0.1:6099
内网服务(NapCat WebUI 等)
每一组「子域名 → 本地端口」就是一条 ingress 规则,改配置后隧道会在几秒内热同步,不需要重启。
三、关键步骤
1. 安装并登录
# 装 cloudflared(Linux amd64 示例)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared
# 用 API Token 而非 OAuth 登录(适合无人值守服务器)
export CF_API_TOKEN="cfat_xxx"
2. 创建/复用隧道
# 已有隧道直接用它的 token 跑
cloudflared tunnel run --token-file /etc/cloudflared/token
# 云端配置模式下,隧道配置在 CF 账号里,本地只需要 token 文件
3. 加域名(两种方式)
方式 A:CF API 写隧道配置 + DNS(适合批量)
# 更新隧道 ingress(新增 napcat1~12 → 6099~6110)
PUT /accounts/{ACCT}/cfd_tunnel/{TUN}/configurations
# 给每个子域名建 DNS CNAME → {隧道ID}.cfargotunnel.com
方式 B:Dashboard 手动加 Public Hostname,选服务类型 HTTP 填 localhost:端口即可。
4. 批量加 12 个子域名的心得
把「预留端口」和「子域名」一一对应提前配好,后面新增实例直接对号入座,不用再动隧道。实测未监听端口返回 502,监听正常返回 301/200——用这个就能判断隧道通没通、实例起没起。
四、踩过的坑
- Token 权限分读写:一个只读 Token 能查 zone/tunnel,但
PUT configurations直接 1001 Not authorized、建 DNS 10000 Authentication error。要写操作得给 Token 加Account → Cloudflare Tunnel → Edit和Zone → DNS → Edit。 - 远程配置模式 vs 本地配置:
tunnel run --token是云端配置;想本地维护 ingress 要写 config.yml + credentials。混用会一脸懵,明确你用的哪种。 - 容器里的 localhost 不是宿主机:隧道转发到容器的服务时,ingress 的 service 写
127.0.0.1:port指的是 cloudflared 所在宿主机,容器要开对应端口映射。
五、安全性
隧道本身不开任何入站端口,比 frp/端口映射安全一个量级。但应用层防护不能省:管理面板要设强密码/Token、保持更新,敏感服务建议再套一层 Cloudflare Access(Zero Trust)做二次认证。
六、小结
Cloudflare Tunnel 适合「一台无公网 IPv4 的服务器 + 一堆内网服务」的场景,配置即代码、免费、还能隐藏源站。强烈建议把 frp 换成它。