Nginx Proxy WebSocket: Your Essential Guide to Real-Time Communication
WebSocket technology has revolutionized real-time web applications, enabling persistent, two-way communication between clients and servers. Whether you're building a chat app, a live dashboard, or a collaborative tool, Nginx can act as a robust proxy for your WebSocket connections. This guide will demystify the process of configuring Nginx for WebSocket proxying, covering essential directives, common pitfalls, and best practices for performance and security.
Understanding WebSocket Handshake and Nginx's Role
Unlike traditional HTTP requests, which are typically short-lived and serve a single purpose, WebSocket connections begin as an HTTP request but then upgrade to a persistent, full-duplex protocol. This upgrade mechanism, defined in RFC 6455, requires special handling by intermediary servers like Nginx.
Here's a simplified breakdown of the process:
- Client Initiates: The client sends an HTTP request to the server, including specific headers like
Upgrade: websocketandConnection: Upgrade. - Server Responds: If the server supports WebSockets, it responds with a
101 Switching Protocolsstatus code, signaling the upgrade. - Connection Established: The connection is now upgraded to a WebSocket connection, allowing for continuous, bidirectional data flow without the overhead of repeated HTTP requests.
Nginx, as a reverse proxy, sits between the client and your backend WebSocket server. Its primary role is to facilitate this upgrade handshake and then tunnel the WebSocket traffic. However, due to how Nginx handles hop-by-hop headers (headers that apply only to a single network hop), it needs explicit configuration to pass the Upgrade and Connection headers to the backend server. Without this, the handshake fails, often resulting in a 400 Bad Request or 426 Upgrade Required error.
Core Nginx Configuration for WebSocket Proxying
To enable Nginx to proxy WebSocket connections effectively, several key directives are crucial within your location block:
proxy_http_version 1.1;: WebSockets require HTTP/1.1 for the upgrade mechanism. Older HTTP versions do not support this.proxy_set_header Upgrade $http_upgrade;: This directive explicitly forwards theUpgradeheader from the client to the backend server. Nginx, by default, strips this header.proxy_set_header Connection "upgrade";: Similarly, this directive forwards theConnectionheader, signaling to the backend that the connection should be upgraded. The value is set to"upgrade"to match the client's request.proxy_pass http://your_backend_server;: This directive specifies the address of your backend WebSocket application.
Example Basic Configuration:
http {
upstream websocket_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 80;
server_name yourdomain.com;
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-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Other locations for regular HTTP traffic
location / {
# ... your regular proxy settings ...
}
}
}
Handling Timeouts for Long-Lived Connections
WebSocket connections are designed to be long-lived, enabling continuous communication. However, Nginx has default timeouts that can prematurely close these persistent connections if they remain idle. The most common issue is connections dropping after approximately 60 seconds.
To address this, you need to increase the proxy_read_timeout directive:
proxy_read_timeout 3600s;(or a higher value, e.g., 1 hour): This setting prevents Nginx from closing the connection if no data is received within the specified duration.
It's also recommended to increase proxy_send_timeout and proxy_connect_timeout for very long-lived connections.
Important Note: While increasing Nginx timeouts is crucial, your backend application should also implement ping/pong mechanisms to keep the connection alive and check its status periodically, with intervals shorter than the Nginx timeout.
Secure WebSocket Connections (WSS/SSL/TLS)
For production environments, securing your WebSocket traffic with SSL/TLS (using the wss:// protocol) is essential. Nginx excels at handling SSL termination, allowing your backend servers to communicate over unencrypted ws:// while clients connect securely via wss://.
This is achieved by configuring a standard SSL server block in Nginx:
server {
listen 443 ssl http2;
server_name ws.yourdomain.com;
ssl_certificate /path/to/your/fullchain.pem;
ssl_certificate_key /path/to/your/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# ... other SSL settings ...
location /ws/ {
proxy_pass http://127.0.0.1:3000;
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-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# Optional: Redirect HTTP to HTTPS
server {
listen 80;
server_name ws.yourdomain.com;
return 301 https://$host$request_uri;
}
In this setup, Nginx handles the TLS encryption/decryption, and then forwards the traffic to your backend application over plain HTTP/WebSocket.
Load Balancing WebSocket Servers
When you need to scale your WebSocket application, load balancing across multiple backend servers becomes necessary. WebSocket connections are stateful, meaning a client should ideally maintain a persistent connection with the same backend server throughout its session. This is achieved using sticky sessions (also known as session affinity).
NGINX Plus and some Nginx open-source configurations support IP hashing for sticky sessions:
ip_hash;(within theupstreamblock): This ensures that requests from the same IP address are consistently directed to the same backend server.
Example with Load Balancing:
upstream websocket_servers {
ip_hash;
server backend1.example.com:3000;
server backend2.example.com:3000;
server backend3.example.com:3000;
}
server {
listen 80;
server_name yourdomain.com;
location /ws/ {
proxy_pass http://websocket_servers;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# ... other headers ...
}
}
It's important to note that WebSocket connections cannot be transparently failed over; clients must reconnect if a backend server becomes unavailable.
Advanced Configurations and Troubleshooting
1. Handling Both HTTP and WebSocket Traffic on the Same Port:
If your application serves both regular HTTP requests and WebSocket connections on the same domain and port, you can use the map directive to dynamically set the Connection header based on whether an Upgrade header is present:
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://your_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade; # Use the mapped variable
# ... other headers ...
}
}
}
This setup ensures that regular HTTP requests get Connection: close, while WebSocket requests correctly receive Connection: upgrade.
- Disable Buffering: For high-traffic WebSocket locations, disabling proxy buffering can sometimes improve performance by reducing latency. Add
proxy_buffering off;to the relevantlocationblock. - TCP_NODELAY: Nginx automatically enables
TCP_NODELAYwhen switching to upgrade mode, which is beneficial for low-latency communication. - Worker Connections: Ensure your
worker_connectionssetting in theeventsblock is adequately high to handle the expected number of concurrent connections.
3. Common Troubleshooting Steps:
- 400 Bad Request / 426 Upgrade Required: This almost always points to missing or incorrect
UpgradeandConnectionheaders, or an incorrectproxy_http_version. Double-check yourproxy_set_headerdirectives and ensureproxy_http_version 1.1is set. - Connection Drops after 60 Seconds: Increase
proxy_read_timeout. - 502 Bad Gateway: The backend server is unreachable or not running. Verify its status and the
proxy_passaddress. - Logs: Always check Nginx's error logs (
/var/log/nginx/error.log) for detailed information. Enabling debug logging can provide even more insights.
Conclusion
Configuring Nginx as a WebSocket proxy is fundamental for building modern, real-time web applications. By understanding the WebSocket handshake, implementing the correct Nginx directives for header forwarding, managing timeouts, and securing connections with SSL/TLS, you can ensure seamless, reliable, and performant bidirectional communication. Remember to always test your configuration thoroughly and consult your Nginx error logs when troubleshooting.
FAQ
**Q: Why do my WebSocket connections drop after 60 seconds?
A:** Nginx has a default proxy_read_timeout of 60 seconds. If no data is exchanged within this period, Nginx closes the connection. You need to increase this timeout (e.g., proxy_read_timeout 3600s;) in your Nginx configuration. Additionally, ensure your WebSocket application sends periodic ping/pong frames to keep the connection active.
**Q: How do I fix the "400 Bad Request" error when proxying WebSockets with Nginx?
A:** This error typically occurs because Nginx, by default, doesn't forward the necessary Upgrade and Connection headers required for the WebSocket handshake. You must explicitly set these headers using proxy_set_header Upgrade $http_upgrade; and proxy_set_header Connection "upgrade"; in your Nginx location block. Ensure proxy_http_version 1.1; is also set.
**Q: Can Nginx proxy both regular HTTP traffic and WebSocket traffic?
A:** Yes, Nginx can handle both. You can configure different location blocks for HTTP and WebSocket endpoints, or use the map directive to dynamically handle both types of traffic within the same location if your application supports it.
**Q: How do I enable SSL/TLS for WebSocket connections (WSS) with Nginx?
A:** Configure a standard Nginx server block with listen 443 ssl; and provide your SSL certificate and key. Then, use proxy_pass to direct WebSocket traffic to your backend. Nginx will handle TLS termination, allowing your backend to use plain ws://.
**Q: What are the performance implications of using Nginx for WebSocket proxying?
A:** Nginx is highly performant and scalable for WebSocket proxying. Tests have shown it can handle tens of thousands of concurrent connections with minimal CPU and memory usage. Performance can be further optimized by disabling proxy buffering for WebSocket locations and ensuring adequate worker_connections are configured.





