用 FRP 内网穿透 WebSocket
WebSocket 不是独立隧道类型。它先走普通 HTTP,再把连接升级成长连接,因此用 NiceFRP 的 HTTP 隧道即可把本地 ws / wss 服务暴露到公网。
适合 Socket.IO、Vite / Webpack HMR、实时聊天、行情推送、IoT 长连接等场景。
选哪种隧道
| 场景 | 选协议 | 公网地址 |
|---|---|---|
| 网页、HMR、Socket.IO、需要 HTTPS | HTTP | wss://<隧道ID>.<节点>.tunnel.nicefrp.com |
| 裸 TCP 客户端,自己处理 TLS | TCP | ws://<节点IP>:<远程端口> |
| 本地服务已经是 HTTPS,要端到端证书 | HTTPS(自备证书) | wss://...,TLS 在你这边终止 |
推荐默认用 HTTP:节点前置 Caddy 会终止 TLS,平台提供证书,浏览器里直接用 wss://,避免混合内容(HTTPS 页面禁止连接 ws://)。
不要为 WebSocket 单独再开一条 TCP 隧道,除非客户端不是浏览器、也不走 HTTP Upgrade。
步骤
1. 先让本地 WebSocket 服务跑起来
例如 Node 在 127.0.0.1:8080 提供 /ws:
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ port: 8080, path: "/ws" });
wss.on("connection", (socket) => {
socket.send("hello from intranet");
socket.on("message", (data) => socket.send(`echo: ${data}`));
});浏览器或本机先确认:
# 需要 websocat 或同类工具;也可用浏览器控制台
websocat ws://127.0.0.1:8080/ws2. 在控制台创建 HTTP 隧道
- 打开 控制台 → 隧道 → 创建隧道。
- 协议选 HTTP(托管 TLS)。
- 记下公网域名,形如
https://<隧道ID>.us2.tunnel.nicefrp.com。
3. 下载配置并指向本地端口
在隧道详情里点 连接,本地地址填 127.0.0.1,端口填 WebSocket 服务端口 (上例为 8080),下载 nicefrp-<tunnel-id>.toml 后运行:
frpc -c nicefrp-<tunnel-id>.toml看到 start proxy success 后,公网即可升级 WebSocket。
4. 用 wss:// 连接
页面若通过 HTTPS 打开,客户端必须用 wss://:
const ws = new WebSocket(
"wss://<隧道ID>.us2.tunnel.nicefrp.com/ws",
);
ws.onmessage = (event) => console.log(event.data);
ws.send("ping");同一条 HTTP 隧道同时提供网页和 WebSocket:HTTP 请求走 /,Upgrade 请求走 /ws(或你应用自己的路径)。frp 按 Host 把流量转到本地端口,不会拆路径。
常见框架
Socket.IO
Socket.IO 会先轮询再升级 WebSocket。把 path 和公网 origin 配对即可:
import { io } from "socket.io-client";
const socket = io("https://<隧道ID>.us2.tunnel.nicefrp.com", {
transports: ["websocket"], // 可选:跳过 polling,减少工作连接突发
});本地服务仍监听 127.0.0.1:3000 之类的 HTTP 端口,隧道协议选 HTTP,不要选 TCP。
Vite / Webpack 热更新(HMR)
开发服务器的 HMR 也是 WebSocket。公网域名和本地 localhost 不同,需要告诉 Vite 客户端去连隧道域名:
// vite.config.ts
export default {
server: {
host: "127.0.0.1",
port: 5173,
hmr: {
protocol: "wss",
host: "<隧道ID>.us2.tunnel.nicefrp.com",
clientPort: 443,
},
},
};隧道本地端口填 5173。HMR 会在短时间打出大量工作连接,NiceFRP 生成的 frpc 配置已把 transport.poolCount 设为 50,一般不必再改。
TCP 隧道怎么用
仅当客户端直连 IP:端口、且不需要平台证书时:
- 创建 TCP 隧道,获得远程端口(例如
20012)。 - 本地仍跑
ws://127.0.0.1:8080。 - 公网连接
ws://<节点公网主机>:20012/ws。
浏览器打开的是 https:// 页面时,这条 ws:// 会被混合内容策略拦截,只适合 CLI、桌面客户端或同为明文 HTTP 的页面。
故障排查
| 现象 | 原因 | 处理 |
|---|---|---|
| 浏览器报 Mixed Content | HTTPS 页面连了 ws:// | 改用 wss://,隧道选 HTTP |
| 一直 pending / 101 失败 | 本地服务未监听,或路径不对 | 先在本机用 ws://127.0.0.1:... 验证 |
| 连上几秒就断 | 中间代理空闲超时 | 应用层做 ping/pong;HTTP 隧道空闲超时已放宽 |
| Vite HMR 连不上 | 客户端仍连 localhost | 按上文配置 server.hmr.host |
| Socket.IO 反复 polling | 被系统代理劫持了 Upgrade | 本机把隧道域名加入代理绕过列表 |
invalid NewWorkConn | 工作连接池瞬时打满 | 使用控制台生成的配置(已含 poolCount = 50) |
系统 HTTP 代理(Clash、Surge 等)有时会把本机 ws://127.0.0.1 也代理掉并 RST。调试本地 WebSocket 时,把 127.0.0.1 和隧道域名加入 bypass。
仍有问题?先跑诊断脚本,或发邮件至 support@nicefrp.com。