nginx 1.3.13+ 原生支持 websocket 反向代理,但默认配置不生效;必须显式配置 proxy_http_version 1.1、透传 upgrade 和 connection 头、关闭缓冲、调大 proxy_read_timeout 和 proxy_send_timeout,否则握手失败或连接被静默中断。

直接说结论:Nginx 1.3.13+ 原生支持 WebSocket 反向代理,但默认配置完全不生效;必须显式设置 proxy_http_version 1.1、透传 Upgrade 和 Connection 头、关闭缓冲、调大超时,否则连接会在几秒内被静默断开。
为什么 WebSocket 握手总失败(101 状态码没返回)
根本原因是 Nginx 默认丢弃逐跳(hop-by-hop)HTTP 头,而 WebSocket 升级依赖两个关键头:Upgrade: websocket 和 Connection: Upgrade。如果这两个头没透传到后端,后端收不到升级请求,就不会返回 101 Switching Protocols,前端就会报 “WebSocket handshake error” 或 “connection header value is not 'upgrade'”。
常见错误配置:
- 只写
proxy_set_header Connection $http_connection—— 错,$http_connection可能是keep-alive,必须固定设为"upgrade" - 漏掉
proxy_http_version 1.1—— HTTP/1.0 不支持Upgrade,Nginx 会降级转发,握手必然失败 - 用
add_header Connection "upgrade"—— 错,这是响应头,不是请求头,对握手无作用
proxy_set_header Connection 该用字符串还是变量
必须用固定字符串 "upgrade",不能用 $http_connection。因为 Connection 是逐跳头,客户端发来的是 Connection: Upgrade, keep-alive,Nginx 默认会清理掉 Upgrade 并只保留 keep-alive 后转发——这直接导致后端收不到升级意图。
更稳妥的做法是配合 map 指令动态构造值:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
然后在 location 中写:
proxy_set_header Upgrade $http_upgradeproxy_set_header Connection $connection_upgrade
这样当客户端没带 Upgrade 头(比如普通 HTTP 请求),$connection_upgrade 就是 close,避免意外隧道化。
长连接中断的三个典型配置坑
WebSocket 是全双工长连接,Nginx 默认行为全是“反长连接”的:
-
proxy_buffering on(默认)—— 会导致帧被缓存、粘包或延迟,必须设为off -
proxy_read_timeout 60(默认)—— 空闲 60 秒就断开,得设成大值,如86400(24 小时) -
proxy_send_timeout 60(默认)—— 后端发心跳或消息慢一点就中断,同样要设大 - 任何
proxy_cache_*配置 —— WebSocket 帧不可缓存,启用即失败
注意:keepalive_timeout 是 Nginx 与客户端的空闲超时,和 WebSocket 无关;真正影响隧道的是 proxy_read_timeout 和 proxy_send_timeout。
SSL/TLS 下 WebSocket(wss://)要额外注意什么
wss 场景下,Nginx 必须终止 SSL 并以明文 HTTP/1.1 转发给后端(除非后端也跑 wss,那得配 proxy_ssl_*,极少见)。重点检查:
- 证书路径是否正确,
ssl_certificate和ssl_certificate_key没写错路径 - 监听配置是否含
ssl,例如listen 443 ssl,否则浏览器会拒绝 wss 连接 - 前端连接地址必须是
wss://,且域名与证书匹配;若用 IP 访问,证书必须含 IP SAN - 不要在
location里加add_header Access-Control-Allow-Origin "*"—— CORS 头必须由后端在 101 响应中返回,Nginx 加了也没用,还可能覆盖后端的正确头
最易被忽略的一点:wss 握手失败时,浏览器开发者工具 Network 面板里看不到 101 响应,只能看 Console 的 WebSocket 错误提示,或者用 tcpdump 抓包确认是否真有 101 返回。











