WebSocket is the workhorse for always-open application data: chat lines, presence, price ticks, collaborative cursors, agent event streams. After a single HTTP upgrade, client and server keep a bidirectional channel for small frames without inventing a new long-poll dance every quarter. It is not a media stack. If you size a VPS the way you would for a video SFU, you overpay for CPU and underinvest in RAM, file descriptors, and proxy timeouts.
This guide covers what WebSocket is, how it differs from plain HTTP, when the architecture needs sticky sessions and Redis, how reverse proxies silently kill idle sockets, and how to host a production service on a European Cloud VPS. Need cameras and microphones? Read What is WebRTC. Still deciding which stack owns which job? See WebRTC vs WebSocket.
What WebSocket is
WebSocket starts as HTTP (or HTTPS). The client asks to upgrade; if the server agrees, the connection stays open and both sides send framed messages in either direction. Frames are cheap. Delivery on a single connection is ordered and reliable because the transport is TCP. That reliability is why synthetic throughput benches often favour WebSocket over a WebRTC DataChannel — you are not paying for codecs, jitter buffers, or SRTP.
Browsers expose a simple API; servers use libraries in Node, Go, Python, Java, and .NET. The operational story is less glamorous: connection counts, heartbeats, backpressure, and horizontal fan-out. Treat WebSocket as a long-lived TCP service with an HTTP handshake, not as “REST with push.”
Protocol-wise you care about a few details in production: ping/pong control frames, close codes, maximum frame size, and whether your library coalesces messages under load. Ignoring close codes turns every deploy into a mystery of silent drops. Ignoring max frame size turns one abusive client into a memory event.
HTTP versus WebSocket
Classic HTTP is request/response. Even with keep-alive, the server cannot freely push until the client asks again (Server-Sent Events are a useful one-way exception). Long polling fakes push by holding requests open; it works until reconnect storms and proxy timeouts make it everyone’s problem.
WebSocket flips the model after upgrade:
- Client and server both send when they have something to say
- No per-message HTTP headers once the socket is up
- One TCP connection can carry many logical subscriptions if your protocol multiplexes topics
You still need HTTPS/WSS on the public internet. Cookies, tokens, and origin checks remain your responsibility. Upgrading does not remove authentication — it only changes how bytes move after the handshake. Many teams authenticate on the upgrade request (Authorization header or short-lived query token) and reject the socket before any application frame is processed.
HTTP remains better for idempotent CRUD, file upload, and cacheable reads. WebSocket wins when the product needs continuous membership in a room, a feed, or a presence set. Mixing both is normal: REST for commands that must be auditable, sockets for the live view.
Use cases that fit
- Chat, typing indicators, and moderation events
- Online/offline presence and fan-out to many subscribers
- Ticker and order-book style updates
- Multiplayer game state (positions and inputs — not voice)
- Dashboard and agent event streams
- Signaling for WebRTC (SDP and ICE candidates) while media rides a separate path
- Collaborative editing awareness (cursors, selections) while documents persist over HTTPS
If the product needs microphones, cameras, or low-latency screen share, do not stretch WebSocket into a DIY media protocol. Keep sockets for control and chat; put media on WebRTC as described in What is WebRTC. The same warning applies to “we will just stream Opus frames over the chat socket” — you inherit TCP head-of-line blocking without gaining ICE or congestion control tuned for interactive media.
Architecture: sticky sessions and Redis
Early traffic fits on one process behind one reverse proxy. Growth forces a choice.
- Single node — fine for early products; app + proxy on one VPS
- Sticky load balancing — pin a socket to an instance (cookie or IP affinity), or terminate WebSocket at a layer that understands upgrades and session ownership
- Redis (or NATS) pub/sub — broadcast across instances so a message published on node A reaches subscribers on node B
Horizontal scale without a shared bus fails in subtle ways: users connected to different nodes never see each other’s events. Vertical scale fails when file descriptors, RAM per connection, or proxy idle timeouts stay at defaults. Plan the bus before the second node, not after the first outage.
Separate concerns early when you can: the process that accepts sockets should not also run heavy batch jobs. Co-locate Redis carefully — memory pressure on the chat box shows up as random disconnects before CPU graphs look scary. If Redis is shared with caches and queues, give the realtime channel its own instance or at least its own memory budget and eviction policy.
Room membership and authorization belong in your app logic, not only in the proxy. A sticky cookie that lands on the wrong tenant’s process is still a bug if you skip per-message ACL checks.
Proxy timeouts and an nginx sketch
Most “WebSocket is flaky” tickets are idle timeouts. Nginx, Caddy, cloud load balancers, and corporate proxies close connections that look dead. Your clients should heartbeat (ping/pong); your servers should close peers that stop answering; your proxy timeouts must sit above the idle interval with margin.
Pass the upgrade headers. Terminate TLS at the proxy or the app — browsers require secure contexts on public sites. A minimal nginx sketch:
location /ws/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
Tune timeouts to your heartbeat. Raise worker_connections and OS file descriptor limits before you celebrate a load-test number from a single laptop. Log disconnect reasons; “1006 abnormal closure” without context wastes days. If you terminate TLS at the edge, forward the original client IP consistently so rate limits and abuse tools see reality.
Health checks should not open a full application WebSocket unless you mean to. Prefer a cheap HTTP health endpoint on the same process, and keep rolling deploys draining sockets with a short grace period instead of hard-killing workers mid-fan-out.
Languages and libraries
The browser exposes a single WebSocket constructor (MDN WebSocket). Servers pick a library in the language you already run:
- Node.js —
ws, or framework helpers (Socket.IO if you want rooms/fallback — know the trade-offs). - Go —
gorilla/websocketornhooyr/websocketfor lean gateways. - Python —
websockets, Starlette/FastAPI WebSocket routes, Django Channels. - Java — Spring WebSocket / Jakarta endpoints for enterprise stacks.
- C# / .NET —
ClientWebSocketand ASP.NET Core WebSockets.
Protocol context across TCP, HTTP, gRPC, GraphQL, WebSockets, and WebRTC is surveyed in Protocols in System Design. Use that map when stakeholders ask “why not just gRPC to the browser?”
Install and operating systems
Clients: every modern desktop and mobile browser implements WebSockets. No plugin. Native apps use the platform socket stack or a language SDK.
Servers: any modern OS works. Production examples are usually Linux behind Nginx or Caddy with HTTP upgrade headers. Windows Server can terminate WebSockets (IIS/ARR or Kestrel) when that is already your estate — the protocol does not require Linux the way many SFUs do.
# Node example
npm install ws
# Go example
go get github.com/gorilla/websocket
Wire TLS at the reverse proxy (wss://). The wire format itself is defined in RFC 6455.
Performance: why benches favour WebSocket
WebSocket rides TCP: ordered, reliable delivery. That is why chat lines do not rearrange themselves and why synthetic throughput tests often crown WebSocket over a WebRTC DataChannel that still pays for ICE and DTLS. The win is real for application data — and irrelevant for interactive video. If a slide deck claims “WebSocket is faster than WebRTC” from a file-copy test, ask whether a microphone was ever opened. Hybrid production chat systems use WebSockets for messages and WebRTC only when voice/video joins the room (Codex: production-ready real-time web chat).
When WebSocket is the wrong tool
Wrong tool signals:
- You need interactive audio/video — use WebRTC (SFU + TURN), not binary frames over a chat socket
- One-way server push of infrequent events — Server-Sent Events may be simpler
- Large file transfer — use ordinary HTTPS uploads and object storage; sockets are a poor CDN
- You only need request/response with occasional updates — plain HTTP may still win on ops simplicity
- You need true peer-to-peer between browsers without a server in the media path — that is WebRTC territory
The industry default for products that combine chat and calls is hybrid: WebSocket (or HTTPS) for signaling and messaging, WebRTC for media. That split is explained end-to-end in WebRTC vs WebSocket. Choosing WebSocket for everything “because the team already knows Node” is how launch week becomes a UDP and TURN project under another name.
Hosting WebSocket services in Europe
Text frames are small; concurrent connections and RAM dominate. Hosting in Europe keeps EU users on a short RTT for presence and chat without forcing every socket through another continent. You still want stable networking and predictable reconnect behaviour more than raw CPU clocks.
Resource profile:
- Enough RAM for peak concurrent sockets plus headroom
- CPU stays modest unless you transform heavy JSON on every message
- Fast disk only if you persist on the same box (usually you should not)
- Sane keepalive, heartbeat, and backpressure settings
- Headroom for reconnect storms after a bad mobile handoff or a deploy
Benchmark concurrent connections and reconnect storms, not “max MB/s of video.” Traffic quotas on cloud plans are usually generous for text; they become a problem if you abuse the socket for bulk binary blobs. Push large payloads to object storage and send a URL or event id on the socket instead.
On EuroVDC Cloud Server plans, practical baselines:
- Cloud VPS Medium — 4 vCPU / 4 GB — early chat and notification services; catalog around €44.99/mo (ex-VAT; confirm on the product page)
- Cloud VPS Medium Plus — 4 vCPU / 8 GB — comfortable default once concurrency grows; catalog around €66.99/mo
- Cloud VPS Medium Max or Large Plus — 16–24 GB RAM — high concurrent sockets, Redis co-located with care
Prefer RAM headroom over chasing vCPU counts. When a single node saturates file descriptors or Redis memory, add a second sticky node and a shared bus before you buy a media-class XL box you do not need. Soft latency check from our Sofia datacenter: Looking Glass.
Operator checklist
- WSS on the public hostname with a trusted certificate
- Proxy upgrade headers and idle timeouts above heartbeat interval
- Auth on the handshake (token query, cookie, or first message) — reject anonymous sockets early
- Heartbeat both ways; reap dead peers; limit message size and rate
- Raise
ulimit -n/ systemdLimitNOFILEbefore load tests - Plan sticky sessions + Redis (or equivalent) before the second app node
- Separate large uploads to HTTPS; keep the socket for events
- Instrument connect, disconnect, and fan-out lag; alert on reconnect storms
Related guides
Order a European Cloud VPS, terminate WSS correctly, and load-test reconnects from mobile networks — not only the office LAN. For browser media, continue with What is WebRTC. For the decision matrix and hybrid pattern, read WebRTC vs WebSocket.