
你写了一个聊天室,用WebSocket。本地测试,没问题。部署到服务器,Nginx代理转发,连不上了。浏览器报错:WebSocket connection failed,或者连上一会儿就断。
Nginx默认配置是为HTTP设计的,短连接、短超时。WebSocket是长连接,需要升级协议、保持连接不断。改几个参数就能解决。
今天把Nginx代理WebSocket的配置拆开讲清楚。
先看一个数据
根据WebSocket协议设计,连接建立时的HTTP请求头里必须包含两个特殊字段:Upgrade: websocket和Connection: Upgrade。这是从HTTP升级到WebSocket的标准流程。如果Nginx没有正确传递这些头,WebSocket握手就直接失败。
实际生产环境中,超过一半的WebSocket连接问题与反向代理配置有关,而非后端服务本身。
WebSocket与HTTP的区别
HTTP:客户端请求,服务器响应。连接断开。下一次请求,重新连接。
WebSocket:客户端发起一次握手后,连接保持打开。双方可以随时互相发送消息,直到一方主动关闭。
Nginx处理HTTP很擅长,请求来了,响应回去,关闭连接。但WebSocket要求Nginx保持连接长时间不断开,并且要识别升级请求,在两端的TCP连接之间双向转发数据。
普通HTTP反向代理配置,碰上WebSocket,会不识别Upgrade头,直接断开连接。
基础配置
先看一个完整的WebSocket代理配置:
nginx
upstream websocket {
server 127.0.0.1:8080;
keepalive 16;
}
server {
listen 80;
server_name ws.example.com;
location /ws/ {
proxy_pass http://websocket;
# WebSocket 必需的请求头
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时设置(关键)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# 其他常用头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
逐行解读
proxy_http_version 1.1
WebSocket要求HTTP/1.1或更高。Nginx默认用1.0。没这一行,握手直接失败。
proxy_set_header Upgrade $http_upgrade
把客户端请求里的Upgrade: websocket头原样传给后端。Nginx默认不会传这个头。
proxy_set_header Connection "upgrade"
告诉后端“这个连接要从HTTP升级成WebSocket”。Connection头的值是固定的upgrade。
这三行是最小配置集。缺少任何一行,WebSocket都连不上。
超时设置
WebSocket连接建立后,可能会长时间没有任何数据传输,比如用户在看屏幕但没打字。Nginx默认超时60秒,没数据就断开。
proxy_read_timeout 3600s
从后端读取数据的超时时间,设为1小时。WebSocket长连接场景,设长一些。
proxy_send_timeout 3600s
向后端发送数据的超时时间,同样设长。
不设的话,连接静默一分钟就被Nginx切断了。客户端必须自动重连,或者用户再发一条消息时发现连不上,体验极差。
负载均衡
WebSocket支持负载均衡,但有个限制:同一个用户的连接必须一直落在同一台后端服务器上。否则消息路由会错乱。
用ip_hash或sticky保持会话:
nginx
upstream websocket {
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
keepalive 16;
}
ip_hash根据客户端IP分配后端,同一IP的请求固定发到同一台后端。不适用共享出口IP的大型网络,但大部分情况够用。
子协议配置
如果WebSocket使用了子协议(如graphql-ws),需要额外处理请求头中的Sec-WebSocket-Protocol字段:
nginx
proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol;
部分后端依赖这个字段来决定使用哪个子协议,不传递可能导致协议协商失败。
日志排查
配置好了还是连不上,看Nginx日志:
bash
tail -f /var/log/nginx/error.log tail -f /var/log/nginx/access.log
常见错误及含义:
upstream timed out:后端没响应,检查后端服务是否正常运行no live upstreams:所有后端都挂了,健康检查失败- 连接后立即关闭:超时设置太短,或
Upgrade头没传过去
用浏览器开发者工具的Network标签页查看WebSocket握手请求的Response Headers。如果Upgrade头没正确返回,问题大概率在Nginx。
真实案例
一个在线客服系统,用户发消息偶尔收不到。排查发现,Nginx的proxy_read_timeout默认60秒,客服人员有时需要看用户输入超过一分钟,连接就被切断了。用户继续打字时才发现连不上,重新连接后历史消息丢失。
超时改成3600秒后,问题消失。用户长时间不说话,连接也保持着。客服说:“终于不用每半分钟担心断线了。”
高级优化
TLS/SSL:WebSocket over WSS(加密WebSocket)和HTTPS代理配置几乎一样,把listen 443 ssl加上证书即可。注意确保后端能处理WSS,或让Nginx终止SSL后转发普通WS给后端。
缓冲区调优:
nginx
proxy_buffering off;
WebSocket场景建议关闭缓冲,实时转发数据。开启缓冲可能导致延迟增加。
最后一句
WebSocket代理的关键就三行:proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade"。加上合理的超时,其他和普通HTTP反向代理配置差别不大。
如果还不行,去检查一下日志。Nginx会告诉你问题在哪。
下次WebSocket连不上,第一反应不是怀疑代码,先去检查Nginx配置。这三行写对了,90%的问题就解决了。




