How to Implement Secure WebSockets
Establish and maintain secure WebSocket (WSS) connections with proper authentication, origin validation, and message integrity.
Problem
WebSocket connections can be hijacked, lack authentication, or accept messages from unauthorized origins. You need to implement secure WebSockets (wss://) with proper authentication, origin checking, and protection against common attacks.
Prerequisites
- Understanding of WebSocket protocol (RFC 6455)
- Familiarity with TLS and the wss:// scheme
- Knowledge of HTTP authentication and CORS
Steps
-
1. Establish WSS with TLS and authentication
Always use wss:// (WebSocket Secure over TLS), never ws:// in production. Perform authentication during the WebSocket handshake: pass a token in the initial HTTP upgrade request (via Authorization header, query parameter, or Sec-WebSocket-Protocol subprotocol). Validate the token before accepting the upgrade. The server must reject the upgrade (HTTP 401/403) if authentication fails. Use the WebSocket frame encoder/decoder to understand the protocol.
InputGET /ws HTTP/1.1 | Upgrade: websocket | Authorization: Bearer <token>OutputHTTP 101 Switching Protocols (if auth valid) | HTTP 401 (if auth fails) -
2. Validate the Origin header
Check the Origin header on the upgrade request against an allow-list of trusted origins. This prevents Cross-Site WebSocket Hijacking (CSWSH) where a malicious website opens a WebSocket to your server using the victim's browser credentials. Reject requests with missing or untrusted origins with HTTP 403. Do not use CORS for WebSockets — Origin checking is the WebSocket-specific mechanism. Use the URL parser to validate origin format.
InputOrigin: https://trusted.example.comOutputorigin_check: pass | (Origin: https://evil.com -> reject 403) -
3. Implement message validation and rate limiting
Validate every incoming message against a schema (JSON Schema, protobuf) before processing. Reject malformed, oversized, or unexpected message types. Enforce a maximum message size (e.g., 1MB) and rate limit per connection (messages per second). Use ping/pong frames for keepalive and to detect dead connections. Set reasonable timeouts for idle connections. Use the JSON validator and WebSocket frame decoder for message inspection.
Inputmessage: { type: chat, text: hello }Outputvalid: true | processed | (oversized: reject 1009 Message Too Big) -
4. Secure the connection lifecycle
Implement proper connection cleanup: close connections on authentication expiry, set idle timeouts, and handle close codes (1000 normal, 1001 going away, 1008 policy violation, 1011 internal error). Re-authenticate on reconnection. Never send sensitive data in broadcast messages. Log connection events for audit. For multi-server deployments, use a shared session store and authenticate on each server, not just the one that received the upgrade.
Inputtoken expired mid-sessionOutputclose: 1008 Policy Violation | reason: auth expired
Expected result
Secure WebSocket connections with TLS, per-connection authentication, origin validation, message schema enforcement, and rate limiting.
Common errors
- Using ws:// instead of wss:// in production
- Always use wss:// (TLS). ws:// sends all data including tokens in plaintext, vulnerable to interception and modification.
- Not validating the Origin header
- Without Origin validation, any website can open a WebSocket to your server using the user's browser session. Always check Origin against an allow-list.
- Authenticating only at connection, not per-message
- For long-lived connections, the initial token may expire. Re-validate periodically or on sensitive operations. Close connections with expired credentials.
Troubleshooting
- If connections drop frequently, check proxy/load balancer WebSocket support and timeout settings (e.g., Nginx proxy_read_timeout).
- If the Origin check blocks legitimate connections, ensure your CDN/proxy forwards the Origin header correctly.
- For large messages, consider chunking or using a separate file upload channel rather than sending large payloads over WebSocket.
Security limits
- WebSocket connections are stateful and long-lived — a compromised token remains valid until the connection closes or the token is re-validated.
- All authentication and message validation happen server-side; the client must not be trusted to enforce security policies.
- Broadcast messages can leak data to unauthorized clients if not filtered per-recipient.
Related tools
Related glossary
Related tasks
Ready to run it?