Nginx 如何反向代理 WebSocket 并启用 SSL(wss://)?
Nginx 反代 WebSocket + WSS 完整配置:Upgrade/Connection 头映射、SSL 证书、proxy_read_timeout 防连接被掐、心跳建议与 FAQ。
WebSocket 反代的关键是三行:proxy_http_version 1.1 + 两个升级头(Upgrade/Connection)。启用 wss 则像普通 HTTPS 站点一样给 Nginx 配证书——客户端到 Nginx 走 wss,Nginx 到后端可走 ws 或 wss。
完整配置
# http 块:Connection 头的智能映射(普通请求不带 Upgrade 时映射为 close)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name ws.example.com;
ssl_certificate /etc/nginx/ssl/ws.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/ws.example.com.key;
location /ws/ {
proxy_pass http://backend:8080;
# WebSocket 三要素
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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;
# 防长连接被闲置超时掐断(默认 60s)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
逐点解释
Upgrade+Connection头:WebSocket 靠 HTTP 握手里的这两个头完成协议升级;Nginx 默认不带它们转发,握手必然失败(客户端报 400/426 或静默断开)。map的作用是:只在客户端真的带Upgrade时才回Connection: upgrade,避免污染普通请求。proxy_http_version 1.1:WebSocket 要求 HTTP/1.1,旧版本 Nginx 反代默认 1.0,1.29.7 起默认 1.1;显式配置可避免依赖默认值。proxy_read_timeout:默认两次从上游读取之间的超时时间为 60 秒,不是连接总寿命或客户端发送间隔——对聊天室这种"安静期很长"的应用是灾难。调大,并由应用层心跳(ping/pong)配合保活。- wss 终止在 Nginx:客户端↔Nginx 加密(wss://),Nginx↔后端内网可走明文 ws://;跨不可信网段时后端也上 TLS(
proxy_pass https://...)。
验证
# 命令行握手测试
curl --http1.1 --max-time 5 -i -N \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
https://ws.example.com/ws/
# 期望返回 101 Switching Protocols;长连接达到5秒时curl超时退出属此测试边界
# 此命令只测握手,双向帧与ping/pong需WebSocket客户端验证
验证时先确认握手返回 101,再保持连接跨过配置的超时时间,并测试双向消息。Nginx 日志通过 DataKit接入观测云后,可检索握手请求及代理错误;应用还应记录断开原因与消息耗时,HTTP 499/502 不能直接当作全部 WebSocket 断连统计。
常见问题(FAQ)
Q:客户端报 400 或握手后立即断开?
A:可能是升级头、Origin/认证、路径或后端拒绝,应结合握手响应和后端日志检查。按上面的三要素配置检查,特别是用了 map 之后别忘了 Connection $connection_upgrade。
Q:连接建立后固定 60 秒就断?
A:proxy_read_timeout 默认值。调大它,并且应用层实现 WebSocket ping/pong 心跳(30 秒一次较常见),双保险。
Q:负载均衡多个 WebSocket 后端要注意什么?
A:建立后的 WebSocket 连接本身固定在选中的后端,不要求额外粘滞。重连、鉴权或长轮询回退是否需要会话粘滞,取决于应用状态设计;跨节点消息分发可用共享服务,但它与连接粘滞不是等价替代关系。