必须加proxy_http_version 1.1、proxy_set_header upgrade $http_upgrade和proxy_set_header connection "upgrade"三行,缺一不可;否则nginx默认按http/1.0处理、不透传upgrade头且不维持长连接,导致后端收不到升级信号而握手失败,返回400或502。

必须加 proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade 和 proxy_set_header Connection "upgrade" 这三行,缺一不可;否则 WebSocket 握手直接失败,返回 400 或 502。
为什么 WebSocket 握手总在 Nginx 层卡住?
WebSocket 连接始于一个标准 HTTP GET 请求,但带了两个关键头:Upgrade: websocket 和 Connection: upgrade。Nginx 默认按 HTTP/1.0 处理、不透传 Upgrade 类请求头,也不维持长连接,所以后端根本收不到升级信号。
常见错误现象包括:
- 浏览器控制台报
WebSocket connection to 'ws://...' failed: Error during WebSocket handshake: Unexpected response code: 400 - 抓包看到 Nginx 返回了 502 Bad Gateway,且响应体为空
- 后端日志完全没收到任何连接请求
根本原因不是后端挂了,而是 Nginx 拦截并丢弃了升级请求——它没被配置成“认识 WebSocket”。
proxy_set_header Connection 为什么不能写死 "upgrade"?
严格来说,可以写死,但更健壮的做法是用 map 指令动态判断:当客户端没发 Upgrade 头时,Connection 应设为 close,否则 Nginx 会错误地把普通 HTTP 请求也当成升级请求转发,导致后端解析出错。
推荐在 http 块顶部加这段映射:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
然后在 location 中使用:proxy_set_header Connection $connection_upgrade。这样既能支持 WebSocket,又不破坏普通 HTTP 流量。
超时配置为什么动辄设成 3600s 甚至 7d?
WebSocket 是长连接,没有请求-响应周期,Nginx 的默认 proxy_read_timeout(60s)会在空闲 60 秒后主动断开连接,表现为客户端突然掉线、重连频繁。
关键点:
-
proxy_read_timeout控制 Nginx 等待后端响应数据的最长时间,对 WebSocket 来说就是“允许连接空闲多久”,建议 ≥ 客户端心跳间隔 × 3 -
proxy_send_timeout同理,影响服务端推送延迟,也需同步调大 -
keepalive_timeout是客户端到 Nginx 的空闲超时,与 WebSocket 无关,不用改
例如客户端每 30 秒发一次 ping,则 proxy_read_timeout 90; 就够用;若业务要求极低断连率,设为 3600 更稳妥。
HTTPS 下 WSS 代理还要注意什么?
WSS 不是新协议,只是 WebSocket over TLS,Nginx 配置和 WS 完全一致,但必须确保:
- SSL 终止在 Nginx 层(即用
ssl_certificate),后端用ws://即可,别强行配 wss:// - 如果后端也启用了 TLS(即 Nginx → 后端走 wss://),则需额外加
proxy_ssl_verify off;(仅测试环境)和证书路径,但绝大多数场景没必要 - 前端连接地址必须和
server_name一致,否则浏览器可能因 SNI 或证书域名不匹配拒绝连接
最容易被忽略的是 Origin 校验:某些 WebSocket 服务(如 Socket.IO、部分自研网关)会校验 Origin 请求头。Nginx 默认不透传该头,需显式加 proxy_set_header Origin $scheme://$host;,否则后端直接返回 403。











