内网穿透(localhost tunnel)这几年几乎成了每个后端、全栈、AI Agent 开发者的标配。你可能在调试 Stripe Webhook、LINE Bot、GitHub App、微信小程序,或者只是想把本地跑起来的 Streamlit Demo 临时发给同事。选型上主流有三个方向:ngrok 托管服务、Cloudflare Tunnel、自托管方案。其中 tunnelto 属于"自托管派"里最轻的一个——用 Rust + async-io 写成,只需一个二进制文件就能同时跑客户端和服务端。
本文会带你走一遍 tunnelto 的定位、托管版与自建版的对比、以及在 Ubuntu + Nginx + HTTPS 环境下的完整自建流程。

tunnelto 是什么?和 ngrok / Cloudflare Tunnel 有何区别?
tunnelto 的核心主张就一句话:把你本地运行的 web server,通过一个公网 URL 暴露出去。从用户视角,它和 ngrok 几乎一样:
tunnelto --port 3000
执行完就会得到一个类似 https://amused-camel.tunnelto.me 的可分享链接。但和 ngrok 最大的区别在于:
| 维度 | ngrok | Cloudflare Tunnel | tunnelto |
|---|---|---|---|
| 写作语言 | Go | Go | Rust(async-io + tokio) |
| 是否能自托管 | 只有 Enterprise 版 | 服务端不可自托管 | 完全开源、可自托管 |
| 免费 URL | 临时子域名 | 需要自有域名 | 临时子域名 / 自有域名 |
| 付费起步 | 较贵 | 免费(但绑 CF) | 月 4 美元起 |
| 静态子域名 | 付费 | 需 DNS 配置 | 付费或自托管即可 |
tunnelto 最大的优势不是"便宜",而是——客户端和服务端是同一个二进制。自己跑一台 VPS,就能拥有属于自己的 *.yourdomain.com 通道服务。
选择托管版还是自建版?
托管版适合:
- 只是偶尔调试 Webhook,一年也就用几十次;
- 不想维护 VPS / 证书;
- 需要固定子域名但不想折腾 DNS。
自建版适合:
- 对数据流过境第三方服务有顾虑(例如内部测试环境、医疗 / 金融类项目);
- 已经有一台带公网 IP 的 VPS;
- 愿意用 Cloudflare / Let's Encrypt 管理证书;
- 想把多个队友共享的通道统一收拢到自有域名下。
下面的实战部分默认你选择自建版。
自托管实战:Ubuntu + Nginx + Let's Encrypt
1. 准备工作
- 一台 Ubuntu 22.04 / 24.04 VPS,带公网 IP;
- 一个你能改 DNS 的域名(以
example.com为例); - DNS 上添加一条 通配符 A 记录:
*.tunnel.example.com→ VPS IP; - 开放 TCP 80 / 443 / 5000(tunnelto 控制通道)。
2. 安装 tunnelto 服务端
tunnelto 在 GitHub 提供了 Release 二进制,也可以直接用 Cargo 构建:
# 方式 A:下载 Release
curl -L https://github.com/tunneltodev/tunnelto/releases/latest/download/tunnelto-server-linux-x86_64.tar.gz \
| sudo tar -xz -C /usr/local/bin/
# 方式 B:从源码构建
sudo apt install -y build-essential pkg-config libssl-dev
curl https://sh.rustup.rs -sSf | sh
cargo install --git https://github.com/tunneltodev/tunnelto tunnelto_server
3. 用 systemd 守护 tunnelto_server
新建 /etc/systemd/system/tunnelto.service:
[Unit]
Description=tunnelto self-hosted server
After=network.target
[Service]
Environment=CTRL_PORT=5000
Environment=DOMAIN=tunnel.example.com
Environment=ALLOWED_HOSTS=*.tunnel.example.com
ExecStart=/usr/local/bin/tunnelto_server
Restart=on-failure
User=tunnelto
[Install]
WantedBy=multi-user.target
然后执行:
sudo useradd -r -s /usr/sbin/nologin tunnelto
sudo systemctl daemon-reload
sudo systemctl enable --now tunnelto
4. Nginx + HTTPS 反向代理
tunnelto 自身只处理 HTTP + 控制通道,TLS 终止交给 Nginx:
server {
listen 443 ssl http2;
server_name *.tunnel.example.com;
ssl_certificate /etc/letsencrypt/live/tunnel.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tunnel.example.com/privkey.pem;
# WebSocket + 正常 HTTP
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
证书用 certbot + DNS 验证(Cloudflare 或 Route 53 插件)签一张通配符证书,注意不要用 HTTP-01 验证,因为我们要覆盖 *.tunnel.example.com。
5. 客户端连到自建服务
开发者机器上安装同一个 tunnelto 二进制,然后:
tunnelto set-auth --key YOUR_SHARED_SECRET
tunnelto --port 3000 \
--sub hello-demo \
--host tunnel.example.com
此时 https://hello-demo.tunnel.example.com 就会映射到你本地的 3000 端口。

踩坑与优化建议
- 别用 HTTP-01 申请通配符证书:只能走 DNS-01,否则 Let's Encrypt 会直接拒绝
*.通配符。 - 控制端口 5000 默认是明文,如果你的 VPS 暴露在公网,建议用 iptables 仅对内网 / VPN 开放,或者把共享密钥做得足够强。
- WebSocket 超时:Nginx 默认
proxy_read_timeout 60s,长连接应用(如 LINE Bot 消息流)建议调到 3600s。 - 不要在 tunnelto 后面跑压测:tunnelto 设计目标是调试通道,不是生产反向代理,打高并发请用 Nginx 直连后端。
- 和 Cloudflare 一起用时要注意 "Full (strict)" 模式 下证书必须匹配,否则会 521。
什么场景不适合 tunnelto?
- 生产级接入层:请用 Cloudflare Tunnel + Access,或 AWS ALB + PrivateLink;
- 跨地域低延迟:tunnelto 是单服务端结构,没有类似 Cloudflare 的全球 Anycast;
- 超长 TCP 隧道:tunnelto 对非 HTTP 协议支持有限,TCP 透传建议用 frp 或 WireGuard。
总结
tunnelto 最大的亮点是同时拥有"开源自托管"和"简单到两行命令"这两个在 ngrok / Cloudflare Tunnel 上很难兼得的特性。如果你:
- 有一台闲置 VPS,
- 不想把调试流量过境第三方,
- 又懒得折腾复杂的 Cloudflare Zero Trust 策略,
那么用一个下午把 tunnelto 架起来,几乎可以一劳永逸地替换掉 ngrok。对于 AI Agent 开发场景(webhook 回调、LLM 工具调用、MCP 调试),它也足够轻量。
评论