WebSocket connection failed: Error during WebSocket handshake: Unexpected response code: 400
ID: networking/websocket-upgrade-failed
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| any | active | — | — | — |
Root Cause
The HTTP Upgrade handshake for WebSocket failed. The server did not respond with 101 Switching Protocols. Common causes include a reverse proxy stripping the Upgrade and Connection headers, the server not supporting WebSocket on the requested path, or HTTP/2 misconfiguration.
genericWorkarounds
-
92% success Configure the reverse proxy to pass WebSocket Upgrade headers correctly
1. In nginx: location /ws/ { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } 2. In Apache: enable mod_proxy_wstunnel and use ProxyPass ws://backend/ 3. In AWS ALB: WebSocket support is automatic, but verify the target group protocol 4. Reload config: systemctl reload nginx 5. Test: wscat -c wss://host/ws/ -
88% success Use a WebSocket-compatible fallback transport like Socket.IO or SockJS
1. Libraries like Socket.IO and SockJS automatically detect WebSocket support 2. If WebSocket fails, they fall back to long-polling or server-sent events 3. Configure the server to support both WebSocket and fallback transports 4. Socket.IO: const io = require('socket.io')(server, { transports: ['websocket', 'polling'] }) 5. This ensures connectivity even through proxies that do not support WebSocket
Dead Ends
Common approaches that don't work:
-
Force WebSocket over HTTP/2 without checking server support
75% fail
WebSocket over HTTP/2 (RFC 8441) requires explicit server support via SETTINGS_ENABLE_CONNECT_PROTOCOL. Most reverse proxies and load balancers do not yet support this. Standard WebSocket uses HTTP/1.1 Upgrade.
-
Add Connection: Upgrade header manually when behind an HTTP/2 proxy
85% fail
HTTP/2 does not use the Connection and Upgrade headers — they are connection-level headers not valid in HTTP/2. The proxy strips them during HTTP/1.1 to HTTP/2 translation. The WebSocket negotiation must use a different mechanism.