nginx websocket代理出现“数据粘包”的本质是代理层缓冲与配置不当导致帧边界错乱:必须透传upgrade和connection头、关闭proxy_buffering、设proxy_read_timeout和proxy_send_timeout为86400,并确保后端逐帧发送、客户端正确解析。

WebSocket 代理转发中出现“数据粘包”,本质不是 WebSocket 协议本身的问题(WS 是基于 TCP 的全双工协议,本身不定义消息边界),而是 Nginx 在代理过程中未正确透传或缓冲控制导致客户端/服务端对消息边界的解析错乱。排查需从 Nginx 配置、后端服务行为、客户端接收逻辑三方面协同验证。
确认 Nginx 已启用 WebSocket 支持(关键配置)
Nginx 默认不识别 Upgrade 和 Connection: upgrade 头,必须显式透传,否则连接会降级为普通 HTTP,后续帧传输极易异常:
- 确保配置中包含以下三项(缺一不可):
proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";
其中 $http_upgrade 是 Nginx 内置变量,会自动提取客户端请求头中的 Upgrade 值(如 websocket);Connection "upgrade" 必须用双引号包裹,避免被解释为指令而非字符串值。
禁用代理缓冲和读写超时(防截断与合并)
Nginx 默认开启响应缓冲(proxy_buffering on)和超时机制,可能将多个 WebSocket 文本帧合并发送,或在空闲时主动关闭连接,造成“粘连”或“断连假象”:
- 在 location 块中添加:
proxy_buffering off; proxy_buffer_size 4k; proxy_buffers 8 4k; proxy_busy_buffers_size 8k; proxy_read_timeout 86400; # 长连接保持,建议设为大值 proxy_send_timeout 86400;
proxy_buffering off 是关键:它让 Nginx 不缓存后端发来的数据,逐帧透传,避免帧合并;proxy_read/send_timeout 过小会导致连接被误杀,引发重连和帧错位。
检查后端服务是否按 WebSocket 规范分帧发送
粘包感知常源于后端未正确使用 WebSocket API 发送独立消息:
- 例如 Node.js 的
ws库,应调用ws.send(data)每次发送一条完整消息(自动封装为独立文本/二进制帧);若手动拼接字符串再 send,就等于把多条业务消息塞进一个 WebSocket 帧,客户端收到后自然“粘在一起” - Java Spring WebFlux 的
WebSocketSession.sendMessage()同理,每次调用对应一个帧;不要用Flux批量推送未拆分的消息
可在后端加日志,确认每次 send 调用对应的是单个业务逻辑单元(如一个 JSON 对象),而非多个对象拼成的字符串。
用 tcpdump + wireshark 抓包定位真实帧边界
绕过应用层干扰,直接看 TCP 流中 WebSocket 帧是否被 Nginx 修改:
- 在 Nginx 服务器执行:
tcpdump -i any -w ws.pcap port 80 or port 443 and host [后端IP] - 用 Wireshark 打开 pcap,过滤
websocket,查看 Frame Payload Length 和 Masking Key - 对比抓包中帧数量/内容 与 客户端
onmessage触发次数是否一致:若抓包显示 3 个独立帧,但客户端只触发 1 次且 data 是三段拼接,则问题在客户端解析逻辑;若抓包里本就是 1 个大帧,则问题出在后端发送侧
注意:HTTPS 流量需解密才能看到 WebSocket 帧,可配置 Nginx 的 ssl_prefer_server_ciphers on 并用浏览器导出 NSS key log 分析。











