Nginx 反代 WebSocket 节点实战:Trojan+WS 与 VLESS+WS 的握手避坑
Nginx 反代 WebSocket 节点实战:Trojan+WS 与 VLESS+WS 的握手避坑
背景
把 VLESS+WS、Trojan+WS 这类基于 WebSocket 传输的节点挂在 Nginx 后面,是很常见的架构:443 端口由 Nginx 统一接管 TLS,再按路径把流量分发到后端不同的入站端口。配置看着简单,但有几个坑不踩一遍很难意识到。
坑一:Upgrade 头没透传,握手直接失败
WebSocket 握手本质是一次 HTTP Upgrade 请求,Nginx 默认的反代配置不会透传 Upgrade 和 Connection 头,后端收到的就是普通 HTTP 请求,直接 400。
正确做法是显式透传:
location /ws {
proxy_pass http://127.0.0.1:2097;
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;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
proxy_http_version 1.1 是必须的,HTTP/1.0 不支持 Upgrade。
坑二:多路径共用一个端口,路由写错全军覆没
多个入站共用 Nginx 的 443 时,用 path 区分:/ws 走 VLESS,/tj 走 Trojan。location 必须精确对应后端入站配置的 path,差一个斜杠都会 404。建议后端入站的 path 和 Nginx 的 location 用同一份配置清单维护,改一边必须同步另一边。
坑三:Host 头决定订阅链接的对错
这是最隐蔽的一个坑。某些面板会根据反代请求里的 Host 头拼订阅链接:如果 Nginx 把 Host 透传了域名,面板可能生成带域名的节点地址;但如果后端实际监听的是 IP 或内网地址,生成的链接在客户端就连不上。反过来,如果 Host 被固定成 IP,面板生成的链接地址就是 IP,端口和传输参数也可能跟着错位。
我的做法是:订阅链接不依赖面板自动拼写,手写一份客户端配置模板,把地址、端口、TLS、path、SNI 每个字段显式写死,面板只负责提供各入站的密钥和 UUID。这样无论 Nginx 怎么转,客户端配置永远是对的。
坑四:TLS 只在 Nginx 终止,后端保持明文
443 的 TLS 由 Nginx 统一终止,后端入站(如 127.0.0.1:2097)走明文 HTTP 即可。不要在后端也配一遍证书,否则会出现双重 TLS,客户端握手直接失败。原则:TLS 终止点只保留一层。
排查清单
按这个顺序查,基本能覆盖九成问题:
- curl 测试握手:
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" https://域名/路径,看返回码,101 才是握手成功; - 检查 Nginx 的 Upgrade/Connection 透传和 proxy_http_version 1.1;
- 核对 location 路径与后端入站 path 是否完全一致;
- 确认后端端口在监听、防火墙/安全组放行;
- 用后端直连(绕过 Nginx)测试一次,定位问题在反代层还是后端。
小结
Nginx 反代 WebSocket 就三句话:头要透传、路径要对齐、TLS 只终结一次。把订阅链接的手动模板维护好,能省掉大量"明明配置没变、突然连不上"的玄学排查时间。
- 点赞
- 收藏
- 关注作者
评论(0)