WebSocket connection to '…' failed
The browser tried to open a WebSocket and the HTTP Upgrade never completed. Your live UI, chat, or signalling channel never connects.
What you see
WebSocket connection to 'wss://example.com/socket' failed:
Error during WebSocket handshake: Unexpected response code: 400
// or: failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
// or: failed: Mixed Content: The page was loaded over HTTPS…
Firefox often says Firefox can't establish a connection to the server at wss://…. Socket.IO wraps the same failure as websocket error / transport fallback to long-polling.
Checklist
- Scheme and mixed content — pages on
https://must usewss://.ws://is blocked as mixed content. Local http:// pages may use ws:// to localhost. - Reverse proxy forgot Upgrade — nginx needs:
Apache:proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 60s; # or higher for idle socketsRewriteEngine+Upgrade/Connectionpass-through; Caddy usually handles it; Cloudflare WebSockets must be enabled on the zone. - Wrong path or 404/502 on handshake — hit the same URL with curl and look at the status before Upgrade. A 502 from an upstream that is down looks like “connection failed” in the browser. See 502 Bad Gateway.
- TLS / certificate — wss:// fails on expired or name-mismatch certs the same way HTTPS does. Related: certificate has expired, ERR_CERT_COMMON_NAME_INVALID, SSL wrong version number (plain HTTP on a TLS port).
- Auth cookies / CSRF on the handshake — first request is still HTTP. SameSite cookies, missing credentials, or a WAF challenge can return 401/403 and abort Upgrade. CORS does not apply the same way as XHR, but cookie rules do.
- Load balancer without stickiness — Upgrade hits node A; later frames hit node B with no shared state. Enable sticky sessions or shared pub/sub for multi-node apps.
- Idle timeouts — proxies kill quiet sockets; clients should ping or send heartbeats. Sudden close after ~60s is often the proxy, not your app logic.
Quick proof
# Should return 101 Switching Protocols (with a real key)
curl -i -N \
-H 'Connection: Upgrade' \
-H 'Upgrade: websocket' \
-H 'Sec-WebSocket-Version: 13' \
-H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \
https://example.com/socket
If curl gets 200 HTML or 404, the proxy is not forwarding WebSockets. If curl gets 101 but the browser fails, check mixed content, extensions (ERR_BLOCKED_BY_CLIENT), or a service worker.
WebSocket vs WebRTC
| Layer | Failure | Fix focus |
|---|---|---|
| WebSocket | connection to wss:// failed | URL, TLS, Upgrade headers, proxy |
| WebRTC ICE | ICE failed | STUN/TURN, UDP, candidates |
| getUserMedia | NotAllowedError | Permission, HTTPS, devices |
Video-call apps often use a WebSocket for signalling and WebRTC for media. Fix the socket first, then ICE. Blurry video after connect is bandwidth/compression — Wirebook compression lesson.
Related
WebRTC ICE failed · NotAllowedError getUserMedia · Failed to fetch · 502 Bad Gateway · Mixed Content blocked · localhost refused to connect
Questions
What does WebSocket connection to … failed mean?
The browser could not complete the HTTP Upgrade to the WebSocket protocol. Check wss:// on HTTPS pages, proxy Upgrade headers, TLS, and that the server is listening.
Why does it work in curl but not the browser?
Mixed content (ws on https), extensions blocking the host, or cookies/WAF only triggered by a real Origin. Compare the failing request in DevTools → Network.
Do I need CORS headers for WebSockets?
The handshake uses Origin; servers should validate it. It is not the same Access-Control flow as XHR, but a rejected Origin still aborts the socket.
Socket.IO falls back to polling — is that ICE?
No. That is Socket.IO transporting events over HTTP when the WebSocket transport fails. WebRTC ICE is a different stack for media.
Cloudflare / nginx closes after a minute
Raise proxy read timeouts and send application pings. Idle websocket timeouts are a top production footgun.
Is this the same as ICE failed?
No. WebSocket failure is signalling/transport. ICE failed means media candidates could not connect after (or without) signalling.
Published .