400 bad request 报错在 websocket 握手阶段,基本可断定是请求头缺失、非法或被代理截断:sec-websocket-key 格式错误(非24字符base64编码16字节随机值)、upgrade/connection 头缺失或大小写错误、请求头超限(nginx默认8kb)、sec-websocket-version 不匹配(仅支持13),以及 error_log 级别过低导致关键提示未输出。
400 bad request 报错在 websocket 握手阶段,基本可以断定是请求头缺失、非法或被代理截断——不是后端逻辑写错了,而是协议升级的“敲门砖”没递到位。
Sec-WebSocket-Key 是否合规
服务端会校验 Sec-WebSocket-Key 的格式,不合规直接拒掉握手并返回 400,error_log 里常出现 invalid Sec-WebSocket-Key:
- 必须是 base64 编码的 16 字节随机值(即编码后长度为 24 字符,末尾带 =);
- 不能含空格、换行、控制字符或非 base64 字符(如中文、下划线);
- 前端用
new WebSocket(url)时浏览器自动生成,一般不出问题;但若手动构造请求(如用curl或 Postman),需确保生成逻辑正确; - Node.js 的
ws库、GoFrame 的r.WebSocket()、Swoole 的onHandShake都会做这层校验。
Upgrade 和 Connection 头是否完整且大小写敏感
这两个头是协议升级的开关,Nginx 或 Apache 默认会过滤掉它们,导致后端收不到,报错日志常见:the client is not using the websocket protocol: 'upgrade' token not found in 'Connection' header:
-
Upgrade值必须是小写websocket(不是Websocket、WEBSOCKET或ws); -
Connection值必须是Upgrade(首字母大写,其余小写),不能是upgrade、keep-alive, upgrade(除非显式保留); - 如果走 Nginx 代理,必须加
proxy_set_header Upgrade $http_upgrade和proxy_set_header Connection "upgrade",否则这两个头根本不会转发; - Apache 用户要确认
mod_proxy_wstunnel已启用,且ProxyRequests Off已设。
请求头总大小是否超限
Nginx 默认只允许每个请求头最大 8KB(large_client_header_buffers 4 8k),而带大量 Cookie、自定义 Header 或长 Origin 的握手请求容易触发 400 Request Header Or Cookie Too Large:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- error_log 中会出现
client sent too large request或header too large; - 临时验证可调大配置:比如改成
large_client_header_buffers 4 16k; - 长期解法是精简客户端发送的 Cookie(避免全站 Cookie 泄露到 /ws 路径)、删减无用自定义 Header;
- 注意:Swoole 默认限制
package_max_length = 2M,但这是针对帧体,不作用于握手头;真正卡头的是 Nginx 或 Apache 的 header buffer。
Sec-WebSocket-Version 是否匹配
目前唯一广泛支持的版本是 13,其他值(如 7、8、14)多数服务端直接拒绝:
- 浏览器强制发
13,所以纯前端场景几乎不会出问题; - 但测试工具(如
wscat -c ws://... --version 7)、旧版 SDK 或自行封装的客户端可能误设; - GoFrame、ws、uWebSockets 等主流实现默认只认
13,error_log 可能提示unsupported version或静默 400; - 检查方式:用
curl -i -H "Sec-WebSocket-Version: 13" ...对比是否仍报错,可快速隔离版本问题。
真正难排查的,往往不是某一行配置漏了,而是 error_log 级别太低——比如 Nginx 设了 error_log /var/log/nginx/error.log warn,那 invalid Sec-WebSocket-Key 这类细节根本不会记。动手前先确认所有中间件的 log_level 是 info 或更低,并盯着日志实时 tail。










