Nginx WebSocket 代理
WebSocket 让浏览器与服务器维持一条全双工长连接,聊天、实时推送、协同编辑都依赖它。它先走普通 HTTP 握手,成功后协议升级为 ws,Nginx 反代时需要把升级相关的请求头原样透传给后端。
升级原理
客户端在握手请求里带上 Upgrade: websocket 和 Connection: Upgrade,服务端同意后返回 101 Switching Protocols,此后这条连接不再按 HTTP 语义解析,而是双向转发原始帧。Nginx 从 1.3.13 起支持 WebSocket 反代,关键是把这两个头透传、并让转发使用 HTTP/1.1。
基本配置
先定义 map 决定 Connection 头取值,再在 location 里透传升级头并调长超时:
map $http_upgrade $connection_upgrade {
default upgrade; # 客户端要求升级时转发 upgrade
'' close; # 普通请求则置 close,避免误伤 keepalive
}
upstream ws_backend {
server 127.0.0.1:8081; # Node/Java 等后端 WebSocket 服务
}
server {
listen 80;
server_name ws.example.com;
location /ws/ {
proxy_pass http://ws_backend;
proxy_http_version 1.1; # 升级依赖 HTTP/1.1,默认 1.0 不行
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 3600s; # 空闲超时调长(默认 60s 会掐断空闲连接)
proxy_send_timeout 3600s;
}
}
用 curl 模拟一次完整握手验证(返回 101 即升级成功):
curl -i -N --max-time 5 \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
-H "Sec-WebSocket-Version: 13" http://example.com/ws/
# 输出:HTTP/1.1 101 Switching Protocols
常见坑
| 现象 | 原因 | 处理 |
|---|---|---|
| 连接频繁断开 | proxy_read_timeout 默认 60s,空闲即被掐 | 调长超时,或客户端按周期发心跳 |
| 后端只收到普通 GET,升级失败 | 多级代理链路中某层丢了 Upgrade/Connection 头 | 每一层代理都要重新透传这两个头 |
| 一直 400/502 | 忘了 proxy_http_version 1.1 | 显式声明 1.1 |
| 握手成功但很快 502 | 后端服务未监听或 upstream 端口写错 | 核对端口与进程 |
小结
WebSocket 反代四件套:map 生成 Connection 头、proxy_http_version 1.1、透传 Upgrade/Connection、调长读写超时。遇到断连先查超时与心跳,多级链路则逐层确认头部是否透传。