Nginx 如何反向代理 WebSocket 并启用 SSL(wss://)?

Nginx 反代 WebSocket + WSS 完整配置:Upgrade/Connection 头映射、SSL 证书、proxy_read_timeout 防连接被掐、心跳建议与 FAQ。

最佳实践
Nginx 如何反向代理 WebSocket 并启用 SSL(wss://)?封面

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 连接本身固定在选中的后端,不要求额外粘滞。重连、鉴权或长轮询回退是否需要会话粘滞,取决于应用状态设计;跨节点消息分发可用共享服务,但它与连接粘滞不是等价替代关系。

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台