Websockets
Chapter 21: WebSocket Connections
Section titled “Chapter 21: WebSocket Connections”Overview
Section titled “Overview”WebSockets provide persistent connections for real-time blockchain updates.
21.1 Why WebSockets?
Section titled “21.1 Why WebSockets?”| Feature | HTTP | WebSocket |
|---|---|---|
| Connection | Request-Response | Persistent |
| Latency | Higher | Lower |
| Real-time | Polling needed | Push-based |
| Use case | Queries | Live data |
21.2 Ethereum WebSocket
Section titled “21.2 Ethereum WebSocket”Connect
Section titled “Connect”const ws = new WebSocket('ws://localhost:8546');Subscribe to New Blocks
Section titled “Subscribe to New Blocks”ws.onopen = () => { ws.send(JSON.stringify({ jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'], id: 1 }));};
ws.onmessage = (event) => { const data = JSON.parse(event.data); console.log('New block:', data.params.result);};Subscribe to Pending Transactions
Section titled “Subscribe to Pending Transactions”ws.send(JSON.stringify({ jsonrpc: '2.0', method: 'eth_subscribe', params: ['newPendingTransactions'], id: 2}));21.3 WebSocket Flags
Section titled “21.3 WebSocket Flags”geth \ --ws \ --ws.addr 0.0.0.0 \ --ws.port 8546 \ --ws.api eth,net,web3 \ --ws.origins "*"# ⚠️ geth has no --ws.ratelimit flag — per-client WS limits are set at the# reverse proxy (Nginx/HAProxy), see Chapter 22.21.4 How WebSocket Connections Work
Section titled “21.4 How WebSocket Connections Work”WebSockets are not a blockchain concept — they are a standard full-duplex protocol (RFC 6455). Nodes use them as a transport for the same JSON-RPC methods as HTTP.
┌─────────────────────────────────────────────────────────────────┐│ WEBSOCKET LIFECYCLE │├─────────────────────────────────────────────────────────────────┤│ ││ CLIENT SERVER (geth WS) ││ │ │ ││ │ HTTP Upgrade Request │ ││ ├──────────────────────────▶│ 101 Switching Protocols ││ │◀──────────────────────────┤ ││ │ ★ CONNECTION OPEN │ ││ │ │ ││ │ JSON-RPC: eth_subscribe │ ││ │ {"params":["newHeads"]} │ ││ ├──────────────────────────▶│ ││ │ │ subscription id: "0x1" ││ │◀──────────────────────────┤ ││ │ │ ││ │ push: new block header │ ││ │◀──────────────────────────┤ ││ │ push: new block header │ ││ │◀──────────────────────────┤ ││ │ │ ││ │ JSON-RPC: eth_unsubscribe│ ││ ├──────────────────────────▶│ ││ │◀──────────────────────────┤ true ││ │ CLOSE frame │ ││ ├──────────────────────────▶│ ││ │ │ │└─────────────────────────────────────────────────────────────────┘Key mechanics every operator should know:
- Upgrade only: a WS session starts as a plain HTTP request with an
Upgrade: websocketheader; the server responds101. - Frames: messages travel in frames (text/binary). JSON-RPC over WS is text frames; binary frames keep room for compression (Eth/65-era wire optimizations).
- Heartbeats (ping/pong): proxies, LBs and clients send
pingframes; the peer must answerpongor the connection is dropped. Long-idle subscriptions can be killed by firewalls/LBs that don’t see traffic — many apps send a ping/alive signal. - Ordering: WS is ordered — you will not see a reordering of events within one connection, which is why WS is preferred for
pendingTransactions/reorg-sensitive apps over polling.
21.5 Subscription Types (Ethereum eth_subscribe)
Section titled “21.5 Subscription Types (Ethereum eth_subscribe)”| Subscription | params | Payload | Use case |
|---|---|---|---|
| New block headers | ["newHeads"] | {parentHash, number, ...} | Block watchdogs, indexers |
| Pending transactions | ["newPendingTransactions"] | tx hash | Mempool monitoring, front-running detection |
| Logs | ["logs", {address, topics}] | matching event logs | Dapp event streams, indexers |
| Syncing | ["syncing"] | {syncing, status} | Detect sync state changes live |
// Subscribe to logs for a specific contractws.send(JSON.stringify({ jsonrpc: '2.0', method: 'eth_subscribe', params: ['logs', { address: '0xContractAddress', topics: [] }], id: 3}));21.6 Production WebSocket Considerations
Section titled “21.6 Production WebSocket Considerations”Origins & CORS
Section titled “Origins & CORS”--ws.origins whitelists browser origins. Use exact origins, not "*", in production:
--ws.origins "https://app.example.com,https://admin.example.com"Load Balancers Must Keep Connections Alive
Section titled “Load Balancers Must Keep Connections Alive”WS is a long-lived connection — an LB that round-robins by HTTP request will break it. Use sticky sessions / IP hash / least-connections for the WS backend, and set the idle timeout higher than your longest expected silent subscription (or rely on ping/pong to keep the path alive).
# nginx — WS sticky by IP hash, generous timeoutupstream geth_ws { ip_hash; server 10.0.0.1:8546; server 10.0.0.2:8546;}server { listen 8546; location / { proxy_pass http://geth_ws; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600s; }}TLS Termination
Section titled “TLS Termination”Terminate TLS at the proxy (chapter 34) and keep WS behind it: wss:// in production, never plain ws:// over the public internet — subscriptions leak tx hashes and contract events.
Reconnect & Resubscribe
Section titled “Reconnect & Resubscribe”Connections drop; clients must reconnect and re-send eth_subscribe (subscriptions are not persisted server-side). Expect at-least-once semantics: on reconnect your app will see a gap, so re-fetch missed blocks after resubscribing.
21.7 Interview Talking Points
Section titled “21.7 Interview Talking Points”“WebSockets give push-based, ordered, low-latency updates, but they cost server resources per connection and are stateful — LB must be sticky, clients must handle ping/pong, reconnect and resubscribe, and production traffic goes over
wss://. I treat subscription churn (connect/disconnect rate) as a monitoring signal for misbehaving clients.”
Summary
Section titled “Summary”- WebSockets enable real-time updates
- Subscribe to new blocks, pending transactions
- Lower latency than HTTP polling
Last Updated: 2026-10-07