WebSocket Reverse Proxy
Introduction
WebSockets keep a long-lived connection for chat, live updates, and Socket.IO. Nginx must upgrade HTTP to WebSocket and proxy the stream to Node or other backends. This chapter adds the required headers to an existing proxy_pass setup and notes Socket.IO-specific paths.
Prerequisites
- Proxy Headers and Path Rules
- Proxying Spring Boot and Node.js
- A WebSocket app listening on localhost (optional for hands-on)
HTTP Upgrade Flow
Client Nginx Backend
│ GET + Upgrade │ │
├───────────────────────►│ GET + Upgrade │
│ ├───────────────────────►│
│◄───────────────────────┤ 101 Switching │
│ 101 Switching │◄───────────────────────┤
│ (bidirectional) │ (proxied stream) │Normal REST proxy_pass without upgrade headers often fails WebSocket handshakes with 400 or immediate disconnect.
Basic WebSocket location
Code explanation:
Upgrade/Connection— required for protocol switch to WebSocket- Long timeouts — idle connections stay open (86400s = 24h example; tune per app)
Reload and test with browser DevTools or wscat:
npx wscat -c ws://example.com/ws/Use wss:// when TLS is enabled on Nginx.
Map-Based Connection Header (Multiple Routes)
When mixing normal HTTP and WebSocket on same server:
Code explanation:
- Non-upgrade requests get
Connection: close - Upgrade requests get
Connection: upgrade
Socket.IO
Socket.IO uses HTTP polling fallback and path /socket.io/:
location /socket.io/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
include /etc/nginx/proxy_params;
proxy_read_timeout 86400s;
}Ensure Node Socket.IO server path matches. Sticky sessions may help if multiple Node instances—see load balancing chapter.
HTTPS and WebSocket
Client connects to wss:// on port 443. Nginx terminates TLS; upstream stays http://127.0.0.1:3000. No separate SSL on Node for that hop on same server.
Common Failures
| Symptom | Fix |
|---|---|
| 502 after upgrade | Backend not supporting WS; wrong port |
| Immediate disconnect | Missing Upgrade headers; timeout too low |
| Works locally not through Nginx | Path prefix strip—align location and proxy_pass |
| 400 Bad Request | HTTP/1.0 to upstream—set proxy_http_version 1.1 |
Check error log during connect attempt.
FAQ
Does gzip affect WebSocket?
No—upgrade bypasses normal response body handling.
Spring Boot WebSocket?
Same headers on proxy_pass to Tomcat/WebSocket endpoint—often /ws or STOMP path.
HTTP/2 and WebSocket?
Browsers use HTTP/1.1 or HTTP/2 for initial request; upgrade handled per spec—Nginx config above still applies at location level.
Buffering?
proxy_buffering off; sometimes needed for streaming—test if messages delay.
Multiple WS backends?
upstream with ip_hash for stateful connections—stateless WS can round-robin.
Cloudflare in front?
WebSockets must be enabled on CDN orange-cloud settings.