nginx代理websocket的关键是完整透传http升级握手:必须配置proxy_http_version 1.1、proxy_set_header upgrade $http_upgrade、proxy_set_header connection "upgrade",并调大proxy_read_timeout和proxy_send_timeout、禁用proxy_buffering。

WebSocket 代理在 Nginx 中本身不参与握手逻辑,真正需要优化的是 Nginx 对 HTTP 升级请求(Upgrade: websocket)的转发效率和连接复用能力。高频率握手本质是大量短时、并发的 GET 请求触发 101 Switching Protocols,关键不在“处理握手”,而在“不阻塞、不误判、不过早断连”。
确保 Upgrade 头完整透传
Nginx 默认会过滤部分头信息,若后端依赖 Sec-WebSocket-Key 或 Sec-WebSocket-Version 验证,缺失会导致握手失败。必须显式开启透传:
- 在
location块中添加:proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade"; - 避免写成
Connection "keep-alive"或遗漏$http_upgrade变量——它会根据客户端是否带Upgrade: websocket自动为空或为websocket,不可硬编码。
调优连接生命周期与缓冲区
高频握手易暴露默认超时和缓冲短板,引发 502/504 或连接被静默关闭:
- 延长协商阶段超时(非 WebSocket 数据传输):
proxy_read_timeout 86400;(建议设大,实际生效取决于后端响应速度)proxy_send_timeout 86400;
注意:这不是“保持 WebSocket 连接”的设置,而是允许握手响应延迟到达而不中断代理流程。 - 禁用缓冲防止粘包干扰升级判断:
proxy_buffering off;proxy_http_version 1.1;(必需,HTTP/1.0 不支持 Upgrade)
限制速率但不误伤合法握手
直接对 /ws 路径限速可能把批量重连、多标签页等正常场景误判为攻击。更稳妥的做法是:
- 按客户端 IP + URI 组合限速,而非单纯路径:
limit_req zone=ws_handshake burst=30 nodelay;
(zone 需提前在 http 块定义,key 包含$binary_remote_addr$request_uri) - 对已建立的 WebSocket 连接放行:利用
$connection_upgrade变量识别已完成升级的连接,跳过后续限速:set $ws_bypass "";<br> if ($connection_upgrade = "1") { set $ws_bypass "1"; }<br> limit_req zone=ws_handshake burst=30 nodelay if=$ws_bypass;
检查后端健康与日志定位瓶颈
90% 的“握手慢”问题实际出在后端服务响应延迟或连接池耗尽,Nginx 日志是第一线索:
- 开启详细代理日志:
log_format ws_log '$remote_addr - $remote_user [$time_local] "$request" '<br> '"$status" $body_bytes_sent "$http_referer" "$http_user_agent" '<br> '$upstream_addr $upstream_response_time $request_time'; - 重点关注
$upstream_response_time(后端返回 101 的耗时)与$request_time(Nginx 总耗时)差值。若前者远大于后者,说明瓶颈在后端;若两者接近且数值高,需检查 Nginx worker 进程负载、CPU 或文件描述符限制。











