Docs / Web Servers / How to Set Up Nginx as a WebSocket Proxy

How to Set Up Nginx as a WebSocket Proxy

By Admin · Mar 2, 2026 · Updated Apr 23, 2026 · 29 views · 3 min read

How to Set Up Nginx as a WebSocket Proxy

WebSocket connections require special handling at the proxy layer because they upgrade from HTTP to a persistent, full-duplex TCP connection. Nginx supports WebSocket proxying natively, making it an excellent choice for routing WebSocket traffic to backend applications on your Breeze server.

How WebSocket Proxying Works

A WebSocket connection starts as a standard HTTP request with an Upgrade: websocket header. The server responds with a 101 Switching Protocols status, and the connection is then kept open for bidirectional communication. Nginx must forward these upgrade headers to the backend for the handshake to succeed.

Basic WebSocket Proxy Configuration

Create or edit your Nginx server block:

sudo nano /etc/nginx/sites-available/websocket-app.conf
upstream websocket_backend {
    server 127.0.0.1:3000;
}

server {
    listen 443 ssl http2;
    server_name ws.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/ws.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ws.yourdomain.com/privkey.pem;

    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;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Timeout settings for long-lived connections
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }

    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;
    }
}

Key Configuration Details

  • proxy_http_version 1.1 — required because WebSocket uses HTTP/1.1 upgrade mechanism; HTTP/2 proxying handles this differently
  • Upgrade and Connection headers — these two headers trigger the protocol switch; without them the backend never receives the upgrade request
  • proxy_read_timeout — set to a high value (86400s = 24 hours) to prevent Nginx from closing idle WebSocket connections

Using a Map for Dynamic Connection Header

For configurations where both HTTP and WebSocket traffic share the same location block, use a map to conditionally set the Connection header:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    # ...
    location / {
        proxy_pass http://websocket_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 86400s;
    }
}

This approach forwards WebSocket upgrades when the header is present and uses standard connection handling otherwise.

Load Balancing WebSocket Connections

If you run multiple backend instances, use the ip_hash directive to ensure a client always connects to the same upstream server (sticky sessions):

upstream websocket_backend {
    ip_hash;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

Testing the Configuration

sudo nginx -t
sudo systemctl reload nginx

Use a WebSocket testing tool to verify:

# Install wscat
npm install -g wscat

# Connect to your WebSocket endpoint
wscat -c wss://ws.yourdomain.com/ws/

Troubleshooting

  • 400 Bad Request — missing proxy_http_version 1.1 or Upgrade headers
  • Connection drops after 60s — increase proxy_read_timeout beyond the default
  • 502 Bad Gateway — the backend application is not running or not listening on the configured port
  • Mixed content errors — ensure the frontend uses wss:// when the site is served over HTTPS

Was this article helpful?