How do WebSockets work behind proxies and firewalls?
See how WebSockets traverse proxies and firewalls via the HTTP upgrade handshake, why wss:// on 443 helps, and how heartbeats keep connections alive.
Expected Interview Answer
WebSockets start as an ordinary HTTP request with an Upgrade header, so they reuse ports 80/443 and pass through most proxies and firewalls that allow web traffic; the key to reliability is using wss:// (TLS) so intermediaries treat the traffic as opaque HTTPS and don't tamper with the upgrade.
The connection begins with an HTTP/1.1 GET carrying Upgrade: websocket and Connection: Upgrade. A transparent or explicit proxy must forward these headers and switch to tunnel mode; many older or corporate proxies strip Upgrade headers, buffer the response, or time out idle connections, breaking the handshake. Running over wss:// on 443 makes the proxy CONNECT-tunnel encrypted bytes it cannot inspect, which dramatically improves success rates. Even then, firewalls and load balancers often close idle connections, so heartbeats keep the tunnel alive, and a well-built client falls back to alternative transports (e.g., HTTP long-polling via libraries like Socket.IO) when a raw upgrade is blocked.
- Reuses ports 80/443 so no special firewall ports are needed
- wss:// makes traffic opaque so proxies don't strip the upgrade
- Heartbeats keep idle connections from being reaped
- Fallback transports maintain connectivity in hostile networks
- Standard HTTP handshake works with existing web infrastructure
AI Mentor Explanation
A WebSocket through a proxy is like a substitute fielder entering under the umpire's approval before staying on for a long spell. The initial request is the formal permission the officials must acknowledge; once granted, the player operates freely. If a strict official refuses the changeover, the substitution fails, just as a rigid proxy that blocks the upgrade stops the persistent connection from ever forming.
Step-by-Step Explanation
Step 1
Start with HTTP upgrade
The client sends an HTTP GET with Upgrade: websocket and Connection: Upgrade on port 80 or 443.
Step 2
Prefer wss:// on 443
Use TLS so proxies CONNECT-tunnel opaque encrypted bytes and can't strip or rewrite the upgrade.
Step 3
Traverse the proxy
Explicit proxies must honor CONNECT and switch to tunneling; misconfigured ones may buffer or block the handshake.
Step 4
Keep the tunnel alive
Send periodic ping/pong so idle-timeout firewalls and load balancers don't reap the connection.
Step 5
Fall back when blocked
Detect a failed upgrade and switch to HTTP long-polling or another transport so the app still connects.
What Interviewer Expects
- Understanding the HTTP Upgrade handshake mechanism
- Why wss:// on 443 traverses proxies better than ws://
- How proxies can strip Upgrade headers or buffer responses
- Idle timeouts and the role of heartbeats
- Fallback transports like long-polling for hostile networks
- Awareness of load balancers needing sticky/upgrade-aware config
Common Mistakes
- Assuming ws:// works everywhere; plaintext often gets stripped by proxies
- Ignoring proxy idle timeouts and having connections die silently
- Not configuring load balancers to pass Upgrade/Connection headers
- No fallback transport, so blocked networks break the app entirely
- Forgetting sticky sessions when multiple backend nodes are involved
Best Answer (HR Friendly)
“WebSockets start out looking like a normal web request, so they usually slip through the same ports and rules that allow websites. Using the secure encrypted version helps them pass tricky corporate proxies, and apps often send small keep-alive pings and keep a backup method in case a network blocks them.”
Code Example
function connect() {
const ws = new WebSocket('wss://example.com/ws'); // 443, TLS -> proxy-friendly
let opened = false;
ws.onopen = () => { opened = true; };
ws.onclose = () => { if (!opened) startLongPolling(); };
ws.onerror = () => { if (!opened) startLongPolling(); };
return ws;
}
async function startLongPolling() {
// Fallback when the upgrade is blocked by a proxy/firewall
while (true) {
const res = await fetch('/poll', { method: 'GET' });
handle(await res.json());
}
}Follow-up Questions
- What headers make up the WebSocket upgrade handshake?
- Why does wss:// pass through proxies more reliably than ws://?
- How do idle timeouts affect long-lived WebSocket connections?
- What must a load balancer be configured to do for WebSockets?
- When and how would you fall back to HTTP long-polling?
MCQ Practice
1. What makes a WebSocket start out compatible with existing web infrastructure?
The handshake is an HTTP/1.1 GET with Upgrade: websocket, so it reuses ports 80/443 and passes through web-aware infrastructure.
2. Why does wss:// traverse restrictive proxies better than ws://?
With TLS the proxy CONNECT-tunnels encrypted data it cannot read, so it won't strip the Upgrade header or rewrite the response.
3. What commonly kills a working WebSocket over time on corporate networks?
Intermediaries close idle connections; periodic ping/pong heartbeats keep the tunnel active and prevent silent drops.
Flash Cards
How does a WebSocket begin? — As an HTTP/1.1 GET with Upgrade: websocket and Connection: Upgrade, reusing ports 80/443.
Why prefer wss:// through proxies? — TLS makes bytes opaque so proxies tunnel them and can't strip the upgrade or buffer the response.
Why heartbeats matter for traversal — Firewalls and load balancers reap idle connections; ping/pong keeps the tunnel alive.
Fallback when upgrade is blocked — Switch to HTTP long-polling or another transport (e.g., Socket.IO) so the app still connects.
Load balancer requirement — It must forward Upgrade/Connection headers and often needs sticky sessions across backend nodes.