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 时保持空值)
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——漏了这行同样握手失败。proxy_read_timeout:默认 60 秒没有数据就断连——对聊天室这种"安静期很长"的应用是灾难。调大,并由应用层心跳(ping/pong)配合保活。- wss 终止在 Nginx:客户端↔Nginx 加密(wss://),Nginx↔后端内网可走明文 ws://;跨不可信网段时后端也上 TLS(
proxy_pass https://...)。
验证
# 命令行握手测试
curl -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
观测云对照
WebSocket 服务的健康度靠传统"请求成功率"看不出来——连接数、断连率、消息延迟才是核心指标。后端应用接入观测云 APM/RUM 暴露连接数指标,Nginx 访问日志采集后,对 101 握手成功率、异常断连(499/502)设告警,"用户大面积掉线"能在工单涌进来之前被发现。
常见问题(FAQ)
Q:客户端报 400 或握手后立即断开?
A:九成是 Upgrade/Connection 头没转发。按上面的三要素配置检查,特别是用了 map 之后别忘了 Connection $connection_upgrade。
Q:连接建立后固定 60 秒就断?
A:proxy_read_timeout 默认值。调大它,并且应用层实现 WebSocket ping/pong 心跳(30 秒一次较常见),双保险。
Q:负载均衡多个 WebSocket 后端要注意什么?
A:WebSocket 是有状态长连接,要么用 ip_hash 保持会话粘滞,要么应用层用 Redis Pub/Sub 等做跨节点消息同步。