Welcome to Part 12 of our comprehensive NGINX on Ubuntu series! We’ll implement WebSocket support and configure NGINX for real-time applications like chat systems, live updates, and bidirectional communication.
WebSocket Fundamentals
WebSockets provide full-duplex communication channels over a single TCP connection, enabling real-time data exchange between clients and servers without the overhead of HTTP polling.
graph TD
A[Client Browser] --> B[NGINX WebSocket Proxy]
B --> C[WebSocket Server]
style B fill:#e1f5fe
graph TD
D[WebSocket Features] --> E[Real-time Communication]
D --> F[Low Latency]
D --> G[Bidirectional Data]
D --> H[Persistent Connection]
style D fill:#e8f5e8
graph TD
I[Use Cases] --> J[Chat Applications]
I --> K[Live Updates]
I --> L[Gaming]
I --> M[Collaborative Editing]
I --> N[Financial Tickers]
style I fill:#fff3e0
graph TD
O[NGINX WebSocket Proxy] --> P[Connection Upgrade]
O --> Q[Header Forwarding]
O --> R[Load Balancing]
O --> S[SSL Termination]
style O fill:#e3f2fdBasic WebSocket Configuration
# Create WebSocket proxy configuration
sudo nano /etc/nginx/sites-available/websocket.example.comupstream websocket_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
# WebSocket connections are stateful, use ip_hash for sticky sessions
ip_hash;
}
server {
listen 80;
server_name websocket.example.com;
# Regular HTTP traffic
location / {
proxy_pass http://websocket_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket endpoint
location /ws {
proxy_pass http://websocket_backend;
# WebSocket specific headers
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;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket timeouts
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
# Disable buffering for real-time communication
proxy_buffering off;
# Optional: Add WebSocket headers for debugging
add_header X-WebSocket-Support "enabled" always;
}
}Advanced WebSocket Configuration
# Create advanced WebSocket configuration with multiple endpoints
sudo nano /etc/nginx/sites-available/advanced-websocket.example.com# Map to handle different WebSocket protocols
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Different backends for different services
upstream chat_websocket {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
ip_hash;
}
upstream notifications_websocket {
server 127.0.0.1:4000;
server 127.0.0.1:4001;
ip_hash;
}
upstream game_websocket {
server 127.0.0.1:5000;
server 127.0.0.1:5001;
ip_hash;
}
server {
listen 80;
server_name advanced-websocket.example.com;
# Rate limiting for WebSocket connections
limit_conn_zone $binary_remote_addr zone=ws_conn:10m;
limit_req_zone $binary_remote_addr zone=ws_req:10m rate=10r/s;
# Main application
location / {
proxy_pass http://chat_websocket;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Chat WebSocket with authentication
location /ws/chat {
# Rate limiting for WebSocket connections
limit_conn ws_conn 10;
limit_req zone=ws_req burst=20 nodelay;
# Authentication check (optional)
# auth_request /auth;
proxy_pass http://chat_websocket/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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;
# Long-lived connection settings
proxy_connect_timeout 1h;
proxy_send_timeout 1h;
proxy_read_timeout 1h;
# WebSocket specific optimizations
proxy_buffering off;
proxy_cache off;
# Custom headers for application
proxy_set_header X-WebSocket-Protocol $http_sec_websocket_protocol;
proxy_set_header X-WebSocket-Version $http_sec_websocket_version;
# Logging for WebSocket connections
access_log /var/log/nginx/websocket-chat.log combined;
add_header X-WebSocket-Service "chat" always;
}
# Notifications WebSocket
location /ws/notifications {
limit_conn ws_conn 5;
limit_req zone=ws_req burst=10 nodelay;
proxy_pass http://notifications_websocket/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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;
proxy_connect_timeout 30m;
proxy_send_timeout 30m;
proxy_read_timeout 30m;
proxy_buffering off;
access_log /var/log/nginx/websocket-notifications.log combined;
add_header X-WebSocket-Service "notifications" always;
}
# Gaming WebSocket with higher performance settings
location /ws/game {
limit_conn ws_conn 20;
limit_req zone=ws_req burst=50 nodelay;
proxy_pass http://game_websocket/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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;
# Shorter timeouts for gaming
proxy_connect_timeout 10s;
proxy_send_timeout 10s;
proxy_read_timeout 1h;
proxy_buffering off;
access_log /var/log/nginx/websocket-game.log combined;
add_header X-WebSocket-Service "game" always;
}
# Health check endpoint
location /health {
access_log off;
return 200 "WebSocket proxy healthy\n";
add_header Content-Type text/plain;
}
}SSL/TLS WebSocket Configuration
# Create secure WebSocket configuration
sudo nano /etc/nginx/sites-available/secure-websocket.example.comserver {
listen 443 ssl http2;
server_name secure-websocket.example.com;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/secure-websocket.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/secure-websocket.example.com/privkey.pem;
# SSL Security Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# Main application
location / {
proxy_pass http://websocket_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Secure WebSocket endpoint (wss://)
location /ws {
proxy_pass http://websocket_backend/ws;
# WebSocket headers
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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;
proxy_set_header X-Forwarded-Proto $scheme;
# SSL-specific headers
proxy_set_header X-Forwarded-SSL on;
proxy_set_header X-Forwarded-Port 443;
# WebSocket timeouts for secure connections
proxy_connect_timeout 1h;
proxy_send_timeout 1h;
proxy_read_timeout 1h;
proxy_buffering off;
# Enhanced logging for secure WebSockets
access_log /var/log/nginx/secure-websocket.log combined;
add_header X-WebSocket-Secure "enabled" always;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name secure-websocket.example.com;
return 301 https://$server_name$request_uri;
}WebSocket Load Balancing
graph TD
A[Client WebSocket] --> B[NGINX Load Balancer]
B --> C[WebSocket Server 1]
B --> D[WebSocket Server 2]
B --> E[WebSocket Server 3]
style B fill:#e1f5fe
graph TD
F[Load Balancing Methods] --> G[IP Hash - Sticky Sessions]
F --> H[Least Connections]
F --> I[Round Robin with Session]
style F fill:#e8f5e8
graph TD
J[Session Management] --> K[Sticky Sessions]
J --> L[Shared State Store]
J --> M[Stateless Design]
style J fill:#fff3e0
graph TD
N[Scaling Strategies] --> O[Horizontal Scaling]
N --> P[Redis Pub/Sub]
N --> Q[Message Queues]
style N fill:#e3f2fd# Create WebSocket load balancing configuration
sudo nano /etc/nginx/sites-available/websocket-lb.example.com# WebSocket servers with health checks
upstream websocket_cluster {
# Use ip_hash for sticky sessions (stateful WebSockets)
ip_hash;
server 192.168.1.10:3000 max_fails=3 fail_timeout=30s;
server 192.168.1.11:3000 max_fails=3 fail_timeout=30s;
server 192.168.1.12:3000 max_fails=3 fail_timeout=30s;
server 192.168.1.13:3000 backup;
keepalive 32;
}
# Alternative: Stateless WebSocket cluster with shared storage
upstream websocket_stateless {
least_conn;
server 192.168.1.20:3000 max_fails=2 fail_timeout=20s;
server 192.168.1.21:3000 max_fails=2 fail_timeout=20s;
server 192.168.1.22:3000 max_fails=2 fail_timeout=20s;
keepalive 32;
}
server {
listen 80;
server_name websocket-lb.example.com;
# Connection limits for WebSocket
limit_conn_zone $binary_remote_addr zone=ws_conn_limit:10m;
limit_conn ws_conn_limit 50;
# Main application
location / {
proxy_pass http://websocket_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Stateful WebSocket with sticky sessions
location /ws/stateful {
proxy_pass http://websocket_cluster/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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;
# Add upstream server info for debugging
add_header X-Upstream-Server $upstream_addr always;
proxy_connect_timeout 1h;
proxy_send_timeout 1h;
proxy_read_timeout 1h;
proxy_buffering off;
}
# Stateless WebSocket with shared state
location /ws/stateless {
proxy_pass http://websocket_stateless/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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;
# Session identifier for stateless design
proxy_set_header X-Session-ID $cookie_session_id;
add_header X-Upstream-Server $upstream_addr always;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 1h;
proxy_buffering off;
}
}WebSocket Monitoring and Debugging
# Create WebSocket monitoring script
sudo nano /usr/local/bin/websocket-monitor.sh#!/bin/bash
# WebSocket Connection Monitor
ACCESS_LOG="/var/log/nginx/access.log"
WS_LOG="/var/log/nginx/websocket.log"
monitor_websockets() {
echo "=== WebSocket Connection Monitor ==="
echo "Generated: $(date)"
echo
# Current WebSocket connections
echo "--- Active WebSocket Connections ---"
local ws_connections=$(ss -tun | grep ':3000\|:4000\|:5000' | grep ESTAB | wc -l)
echo "WebSocket backend connections: $ws_connections"
# NGINX WebSocket proxy connections
local nginx_connections=$(ss -tun | grep ':80\|:443' | grep ESTAB | wc -l)
echo "NGINX proxy connections: $nginx_connections"
echo
# WebSocket upgrade requests (last hour)
echo "--- WebSocket Upgrades (Last Hour) ---"
local hour_ago=$(date -d '1 hour ago' '+%d/%b/%Y:%H')
local current_hour=$(date '+%d/%b/%Y:%H')
local upgrades=$(grep -E "$hour_ago|$current_hour" "$ACCESS_LOG" 2>/dev/null | grep -i "upgrade.*websocket" | wc -l)
echo "WebSocket upgrade requests: $upgrades"
# WebSocket endpoints usage
echo "--- WebSocket Endpoint Usage ---"
grep -E "$hour_ago|$current_hour" "$ACCESS_LOG" 2>/dev/null | grep "/ws" | awk '{print $7}' | sort | uniq -c | sort -nr
echo
# Failed WebSocket connections
echo "--- Failed WebSocket Connections ---"
local failed_ws=$(grep -E "$hour_ago|$current_hour" "$ACCESS_LOG" 2>/dev/null | grep "/ws" | grep -E " 4[0-9][0-9] | 5[0-9][0-9] " | wc -l)
echo "Failed WebSocket requests: $failed_ws"
echo
# Top WebSocket clients
echo "--- Top WebSocket Clients ---"
grep -E "$hour_ago|$current_hour" "$ACCESS_LOG" 2>/dev/null | grep "/ws" | awk '{print $1}' | sort | uniq -c | sort -nr | head -5
echo
}
check_websocket_health() {
echo "=== WebSocket Health Check ==="
echo
# Test WebSocket endpoints
local endpoints=(
"ws://websocket.example.com/ws"
"ws://advanced-websocket.example.com/ws/chat"
"ws://websocket-lb.example.com/ws/stateful"
)
for endpoint in "${endpoints[@]}"; do
echo "Testing: $endpoint"
# Use websocat or curl to test WebSocket connection
if command -v websocat >/dev/null 2>&1; then
timeout 5s websocat --exit-on-eof "$endpoint" <<< "ping" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✅ $endpoint - Connected successfully"
else
echo "❌ $endpoint - Connection failed"
fi
else
# Fallback to HTTP upgrade test
local host=$(echo "$endpoint" | sed 's|ws://||' | sed 's|/.*||')
local path=$(echo "$endpoint" | sed 's|ws://[^/]*||')
curl -s -H "Upgrade: websocket" -H "Connection: Upgrade" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Sec-WebSocket-Version: 13" \
"http://$host$path" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✅ $endpoint - HTTP upgrade successful"
else
echo "❌ $endpoint - HTTP upgrade failed"
fi
fi
done
echo
}
case "${1:-monitor}" in
monitor)
monitor_websockets
;;
health)
check_websocket_health
;;
watch)
while true; do
clear
monitor_websockets
echo "Press Ctrl+C to exit..."
sleep 10
done
;;
*)
echo "Usage: $0 {monitor|health|watch}"
;;
esac
# Make executable: sudo chmod +x /usr/local/bin/websocket-monitor.shSample WebSocket Application
Here’s a simple WebSocket server example. For the complete application code, check our GitHub repository: WebSocket Chat Application
# Quick WebSocket server setup (Node.js example)
# Install dependencies
npm init -y
npm install ws express
# Create simple WebSocket server (server.js)
const WebSocket = require('ws');
const express = require('express');
const http = require('http');
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server, path: '/ws' });
// Serve static files
app.use(express.static('public'));
// WebSocket connection handling
wss.on('connection', (ws, request) => {
console.log('New WebSocket connection from:', request.socket.remoteAddress);
ws.on('message', (message) => {
console.log('Received:', message.toString());
// Broadcast to all connected clients
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
ws.on('close', () => {
console.log('WebSocket connection closed');
});
});
server.listen(3000, () => {
console.log('WebSocket server running on port 3000');
});Complete Application Files: The full WebSocket chat application with HTML client, advanced features, and deployment scripts is available in our GitHub Gist collection.
Testing WebSocket Configuration
# Enable WebSocket sites
sudo ln -s /etc/nginx/sites-available/websocket.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/advanced-websocket.example.com /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload NGINX
sudo systemctl reload nginx
# Test WebSocket upgrade headers
curl -H "Upgrade: websocket" \
-H "Connection: Upgrade" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Sec-WebSocket-Version: 13" \
-H "Host: websocket.example.com" \
http://localhost/ws -v
# Monitor WebSocket connections
/usr/local/bin/websocket-monitor.sh
# Check WebSocket health
/usr/local/bin/websocket-monitor.sh health
# Watch WebSocket traffic in real-time
sudo tail -f /var/log/nginx/websocket-chat.log
# Test with websocat (install with: cargo install websocat)
# echo "Hello WebSocket" | websocat ws://websocket.example.com/wsWhat’s Next?
Excellent! You’ve successfully configured NGINX for WebSocket support and real-time applications. Your server can now handle bidirectional communication, load balance WebSocket connections, and support modern real-time web applications.
Coming up in Part 13: NGINX Microservices Architecture and API Gateway
References
This is Part 12 of our 22-part NGINX series. Your server now supports real-time WebSocket applications! Next, we’ll build a microservices API gateway. Questions? Share them in the comments!
