nginx代理websocket的关键是完整透传upgrade和connection头并防止超时中断。需配置map映射$connection_upgrade、设proxy_http_version 1.1、proxy_set_header upgrade $http_upgrade与connection $connection_upgrade,并关闭缓冲、延长读写超时至86400秒。

要让 Nginx 成功代理 WebSocket,关键不是加几个头就完事,而是让协议升级请求完整、不被截断、不被超时中断。核心问题往往出在 Upgrade 和 Connection 这两个头的处理上——Nginx 默认会丢弃它们,必须显式干预。
Upgrade 和 Connection 头为什么必须透传
WebSocket 握手本质是一个 HTTP 协议升级过程。客户端发的请求里必须带:
- Upgrade: websocket —— 告诉后端“我想换协议”
- Connection: Upgrade —— 告诉中间层“别关连接,我要升级”
这两个是逐跳(hop-by-hop)头,HTTP/1.0 不支持,Nginx 默认也不会转发。如果漏掉任一个,后端收不到升级意图,直接返回 400 或 502,浏览器控制台报 “Error during WebSocket handshake”。
正确配置 Upgrade 和 Connection 的写法
不能硬编码,也不能照搬变量,得按语义精准设置:
- proxy_http_version 1.1; —— 强制走 HTTP/1.1,这是 Upgrade 的前提
- proxy_set_header Upgrade $http_upgrade; —— 用内置变量 $http_upgrade,保留客户端原始值(比如某些 IoT 设备可能发 upgrade: h2c,写死 websocket 反而出错)
- proxy_set_header Connection "upgrade"; —— 注意是固定字符串 "upgrade",不是 $http_connection;后者为空时 Nginx 会塞入 close,直接断连
更稳妥的做法是配合 map 指令做智能判断:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
然后在 location 中写:proxy_set_header Connection $connection_upgrade; —— 这样普通 HTTP 请求仍走 keep-alive,WebSocket 请求才真正升级,互不干扰。
光透传头还不够:配套动作必须跟上
只设对头,但其他默认行为没调,连接照样秒断:
- proxy_buffering off; —— WebSocket 是帧流,缓冲会导致延迟、粘包,甚至握手失败
- proxy_read_timeout 86400; —— 默认 60 秒,空闲 60 秒就断;设为 24 小时可覆盖绝大多数心跳间隔
- proxy_send_timeout 86400; —— 同理,保障服务端主动推送不被拦截
- 不启用任何 proxy_cache_* 指令 —— WebSocket 消息不可缓存,开了必失败
推荐的最小可用 location 配置
建议为 WebSocket 路径单独建 location,比如 /ws 或 /api/ws,避免影响其他接口:
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
注意:$connection_upgrade 需提前在 http 块中定义 map,否则会报变量未定义。











