协议升级失败时未出现101响应,主因是反向代理未透传upgrade头、服务端未正确处理upgrade请求、sec-websocket-key校验失败、tls证书问题或origin校验拦截。

如果您在浏览器中发起 WebSocket 连接请求,但 Network 面板中未看到 HTTP/1.1 101 Switching Protocols 响应,则说明协议升级过程在某一层被中断。该问题通常表现为静默失败——无 JavaScript 报错、onerror 不触发或延迟触发,仅连接状态长期停留在 CONNECTING(0)或直接跳转为 CLOSED(3)。以下是定位与修复此问题的多种途径:
一、检查反向代理(如 Nginx)是否透传 Upgrade 头部
Nginx 等网关默认不识别 WebSocket 协议升级机制,若未显式配置,会将 Upgrade 和 Connection 头过滤或改写,导致后端服务收不到合法握手请求,因而无法生成 101 响应。
1、打开 Nginx 配置文件,定位到对应 location 块(例如 location /ws/ 或 location /api/socket/)。
2、确认已添加以下三行且拼写准确(注意大小写与引号):
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
3、检查 proxy_pass 指向地址是否可达,且后端服务监听地址与端口匹配(例如 http://127.0.0.1:8080)。
4、执行 nginx -t 验证语法,再运行 nginx -s reload 重载配置。
二、验证服务端是否正确处理 Upgrade 请求头
WebSocket 握手依赖客户端发送的两个关键请求头:Upgrade: websocket 和 Connection: Upgrade。若服务端框架(如 Workerman、Swoole、Ratchet 或自研 HTTP 服务器)未校验或忽略这两个字段,将直接返回 400 或 500,跳过 101 响应流程。
1、在服务端入口逻辑中插入日志,打印原始请求头:var_dump($request->getHeader('Upgrade'), $request->getHeader('Connection'));
2、确认输出中 Upgrade 值为 websocket(全小写,无空格),Connection 值包含 Upgrade(大小写敏感)。
3、若任一头缺失或值错误,需检查中间件、路由匹配逻辑或 HTTPS 重定向是否提前终止了原始请求。
4、对基于 PHP-FPM 的部署,确保服务端未运行于 CGI/FastCGI 模式——因进程生命周期极短,无法维持升级后的长连接,必须使用常驻进程模型(如 php start.php start -d)。
三、排查 Sec-WebSocket-Key 校验与响应头污染
服务端收到有效 Upgrade 请求后,必须依据 Sec-WebSocket-Key 计算 Sec-WebSocket-Accept 值,并返回完整响应头。任何计算错误、额外输出(BOM、echo、错误警告、var_dump)都会污染响应体,使浏览器拒绝升级,即便状态码为 101 也无法完成握手。
1、在服务端生成 Sec-WebSocket-Accept 前,调用 ob_end_clean() 清除所有输出缓冲区。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
2、Sec-WebSocket-Accept 必须严格按规范计算:对客户端 Sec-WebSocket-Key 字符串拼接固定 GUID 258EAFA5-E914-47DA-95CA-C5AB0DC85B11,进行 SHA-1 哈希后 Base64 编码。
3、响应中必须包含且仅包含以下必需头:HTTP/1.1 101 Switching Protocols、Upgrade: websocket、Connection: Upgrade、Sec-WebSocket-Accept: [base64-sha1-value]。
4、使用 curl 手动模拟握手请求,观察原始响应字节流:curl -i -N -H "Upgrade: websocket" -H "Connection: Upgrade" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" -H "Sec-WebSocket-Version: 13" http://localhost:8080/ws。
四、确认 TLS/SSL 层未拦截 wss:// 握手
当使用 wss:// 协议时,TLS 握手先于 HTTP 升级发生。若证书无效(自签名、过期、域名不匹配、链不完整),浏览器会在 TCP 层或 TLS 层静默终止连接,Network 面板可能显示 failed 或无任何请求记录,根本不会发出 Upgrade 请求。
1、访问同域名下的普通 HTTPS 页面(如 https://yourdomain.com/health),确认浏览器地址栏显示安全锁图标且无证书警告。
2、使用 openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 检查证书链完整性与有效期。
3、若为本地开发环境,确保已将自签名 CA 证书导入操作系统及浏览器信任库,而非仅在代码中设置 ignore SSL verify。
4、检查负载均衡器(如 AWS ALB、Cloudflare)是否启用了“强制 HTTPS”或“最低 TLS 版本”策略,导致早期 TLS 握手失败。
五、审查跨域与 Origin 校验逻辑
尽管 WebSocket 协议本身不受同源策略限制,但服务端常主动校验 Origin 头以防范 CSRF。若校验失败并返回 403 或直接关闭连接,浏览器将收不到 101 响应,且控制台可能无明确提示。
1、在浏览器 DevTools 的 Network 面板中,点击 WebSocket 请求,查看 Request Headers 中的 Origin 值(如 https://admin.example.com)。
2、检查服务端代码中 Origin 白名单配置,确认该值被显式允许(避免仅允许 *,尤其在携带 Cookie 时无效)。
3、若服务端返回 403,需在响应中添加 Access-Control-Allow-Origin 头(仅对握手阶段的 HTTP 请求生效,不影响后续 WebSocket 数据帧)。
4、禁用浏览器扩展(如广告拦截器、隐私保护插件),某些插件会主动篡改或屏蔽 Origin 头。










