内网穿透方案对比:frp、ngrok 与反向 SSH 隧道
家里有台机器想从外面访问、本地开发要收 webhook、临时给同事演示——这些都需要「内网穿透」。方案很多,选对很重要。
一、先想清楚需求
| 场景 | 推荐方案 |
|---|---|
| 临时演示本地服务(几分钟) | SSH 隧道 / ngrok |
| 长期暴露家中服务 | frp(自建) |
| 收第三方 webhook 调试 | ngrok / cloudflared |
| 公司内网机器远程维护 | frp + 严格鉴权 |
| 不想折腾、要稳定域名 | Cloudflare Tunnel |
二、方案一:SSH 反向隧道(零安装)
只要有一台公网 VPS 和 SSH,不需要装任何东西。
# 在内网机器上执行:把公网机的 8080 转到本机 3000
ssh -N -R 8080:127.0.0.1:3000 user@公网VPS -p 2222
# 之后访问 公网VPS:8080 就等于访问内网机器的 3000公网 VPS 上要允许:
# /etc/ssh/sshd_config
GatewayPorts clientspecified # 允许绑定非 localhostsudo sshd -t && sudo systemctl reload ssh更稳的写法(加上重连):
# 用 autossh 自动重连
sudo apt install -y autossh
autossh -M 0 -N -R 8080:127.0.0.1:3000 user@公网VPS -p 2222 \
-o "ServerAliveInterval=30" -o "ServerAliveCountMax=3"优点:零依赖、几分钟搞定。缺点:断线要重连、只有一个端口、不方便管理。
三、方案二:frp(自建,最灵活)
frp 分服务端(frps,跑在公网机)和客户端(frpc,跑在内网机)。
服务端 /etc/frp/frps.toml:
bindPort = 7000
# 鉴权,一定要设
auth.method = "token"
auth.token = "换成一串随机长字符串"
# 面板(可选,记得改密码)
webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "强密码"# systemd 托管 frps
[Unit]
Description=frp server
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=always
[Install]
WantedBy=multi-user.target客户端 frpc.toml:
serverAddr = "公网VPS的IP"
serverPort = 7000
auth.method = "token"
auth.token = "与服务端一致"
[[proxies]]
name = "home-web"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3000
remotePort = 8080
# 也可以转发 HTTP,配合域名
[[proxies]]
name = "home-http"
type = "http"
localPort = 8080
customDomains = ["home.bzii.cn"]配合 Nginx 反代 + HTTPS:
server {
listen 443 ssl http2;
server_name home.bzii.cn;
ssl_certificate /etc/letsencrypt/live/home.bzii.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/home.bzii.cn/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}四、方案三:Cloudflare Tunnel(免公网 IP)
如果你连公网 VPS 都没有,或者不想开端口,cloudflared 是最省事的:
# 安装 cloudflared,登录后创建隧道
cloudflared tunnel login
cloudflared tunnel create home
cloudflared tunnel route dns home home.bzii.cn
# 配置 ~/.cloudflared/config.ymltunnel: <隧道ID>
credentials-file: /root/.cloudflared/<隧道ID>.json
ingress:
- hostname: home.bzii.cn
service: http://localhost:3000
- service: http_status:404cloudflared tunnel run home
# 或用 systemd 托管
sudo cloudflared service install优点:免费、自带 HTTPS、不需要公网 IP、不需要开防火墙端口。出站连接即可。
五、安全是重点
把内网服务暴露到公网,等于给家里开了一扇门。必须做:
- 强鉴权:token 用
openssl rand -hex 32生成,别用简单密码; - 只转发必要端口,数据库、Redis、SSH 一律不转发;
- 服务端只监听必要地址,frps 的 7000 端口只对客户端 IP 放行:
sudo ufw allow from <客户端出口IP> to any port 7000- 暴露的服务本身要有认证,别让一个没密码的管理界面直接上公网;
- 加一层 HTTPS,明文传输密码等于送人;
- 日志留着,定期看有没有陌生连接。
# 生成强 token
openssl rand -hex 32六、对比表
| 方案 | 需要公网机 | 成本 | 稳定性 | 配置难度 |
|---|---|---|---|---|
| SSH 隧道 | 是 | 0 | 一般(断线重连麻烦) | 低 |
| frp | 是 | VPS 费用 | 好 | 中 |
| ngrok | 否 | 免费额度有限 | 好 | 低 |
| Cloudflare Tunnel | 否 | 免费 | 好 | 中 |
| Tailscale / ZeroTier | 否 | 免费额度 | 很好 | 低 |
补充一句:如果只是你自己要访问家里的机器,用 Tailscale 这类组网工具比穿透更合适——它不把服务暴露给公网,只是把你的设备组成一个虚拟局域网,安全性高一个量级。
七、我的选择
- 自己远程访问家里 NAS / 开发机 → Tailscale;
- 要给别人访问的临时演示 → SSH 隧道 或 ngrok;
- 长期对外的小服务 → frp,前面套 Nginx + HTTPS。
穿透的本质是「让没有公网 IP 的服务被访问到」。想清楚是「给自己用」还是「给别人用」,方案自然就出来了。
