要让nginx正确处理websocket握手,必须显式透传upgrade和connection两个逐跳头部:用$http_upgrade动态传递upgrade头,用"upgrade"或map映射方式设置connection头,并启用proxy_http_version 1.1、关闭缓冲与缓存、延长读写超时。

要让 Nginx 正确处理 WebSocket 握手,核心是把客户端发来的 Upgrade 和 Connection 这两个逐跳(hop-by-hop)头部原样、无干扰地传给后端服务。Nginx 默认会丢弃或重写它们,不干预就会导致 400/502 错误、握手失败或连接秒断。
为什么必须显式配置这两个头
WebSocket 握手本质是 HTTP 协议升级请求:
-
Upgrade: websocket —— 客户端声明“我想切换协议”,值可能是
websocket、h2c或自定义协议名,不能硬编码 - Connection: Upgrade —— 告诉中间代理“别关闭连接,我要升级”,这是触发 101 响应的关键信号
- 这两个头属于逐跳头,HTTP/1.0 不支持,Nginx 默认不转发,必须手动放行
Upgrade 头的正确写法
用 Nginx 内置变量 $http_upgrade 动态透传,保留客户端原始值:
- ✅ 正确:
proxy_set_header Upgrade $http_upgrade;(自动捕获Upgrade的实际值) - ❌ 错误:
proxy_set_header Upgrade "websocket";(覆盖非标准客户端请求,如 IoT 设备发mqtt或chat) - ⚠️ 注意:该变量在普通 HTTP 请求中为空,安全无副作用
Connection 头的两种可靠写法
不能直接用 $http_connection,它常含多个值(如 "Upgrade, keep-alive"),Nginx 清理后只剩 keep-alive,后端收不到升级意图:
- ✅ 简单写法:
proxy_set_header Connection "upgrade";(固定小写字符串,引号不可省) - ✅ 更稳妥写法(推荐):
在 http 块中定义:map $http_upgrade $connection_upgrade {<br> default upgrade;<br> "" close;<br>}
在 location 中使用:proxy_set_header Connection $connection_upgrade;
这样普通请求走 keep-alive,WebSocket 请求才真正升级,互不干扰
配套设置缺一不可
只设对头还不够,Nginx 默认行为仍会中断长连接:
-
proxy_http_version 1.1;—— 强制走 HTTP/1.1,是 Upgrade 的前提 -
proxy_buffering off;—— 关闭缓冲,避免帧粘包或握手延迟 -
proxy_read_timeout 86400;和proxy_send_timeout 86400;—— 防止空闲或大消息被超时切断 - 禁用所有
proxy_cache_*指令 —— WebSocket 消息不可缓存,启用即失败











