How To Set Up NGINX As A Reverse Proxy For Secure WebSockets?

Published July 12, 2024

Problem: Securing WebSocket Connections with NGINX

Setting up a reverse proxy for WebSocket connections can be hard, especially when you want to keep it secure. NGINX is a web server that can do this job, but you need to know how to set it up correctly.

Configuring NGINX for Secure WebSocket Proxy

Setting Up NGINX Installation

To set up NGINX for a secure WebSocket proxy, you need to install it on your server. Here are the steps:

  1. Update your package manager:

    sudo apt update
  2. Install NGINX:

    sudo apt install nginx
  3. Start the NGINX service:

    sudo systemctl start nginx
  4. Enable NGINX to start on boot:

    sudo systemctl enable nginx

SSL/TLS Certificate Configuration

For secure WebSocket connections, you need SSL/TLS certificates. Here's how to get and configure them:

  1. Install Certbot for free Let's Encrypt certificates:

    sudo apt install certbot python3-certbot-nginx
  2. Get a certificate for your domain:

    sudo certbot --nginx -d yourdomain.com
  3. Follow the prompts to complete the certificate installation.

NGINX Configuration for WebSocket Proxy

Now, set up NGINX to proxy WebSocket connections:

  1. Open the NGINX configuration file:

    sudo nano /etc/nginx/sites-available/default
  2. Add this configuration to enable WebSocket support:

    server {
       listen 443 ssl;
       server_name yourdomain.com;
    
       ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
       ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    
       location /websocket/ {
           proxy_pass http://backend_host;
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";
       }
    }

    Replace yourdomain.com with your domain and backend_host with the address of your WebSocket server.

  3. Save the file and exit the editor.

  4. Test the NGINX configuration:

    sudo nginx -t
  5. If the test passes, reload NGINX:

    sudo systemctl reload nginx

This configuration sets up NGINX to listen on port 443 for SSL connections, use the SSL certificates you obtained earlier, and proxy WebSocket connections to your backend server.

Example: Handling WebSocket Timeouts

To prevent WebSocket connections from timing out, you can add these directives to your NGINX configuration:

location /websocket/ {
    proxy_pass http://backend_host;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}

This sets the read and send timeouts to 1 hour (3600 seconds), which helps maintain long-lived WebSocket connections.

Implementing WebSocket Proxy Settings

Proxy Pass Directives

To set up the proxy_pass directive for WebSocket traffic in NGINX:

  1. Open your NGINX configuration file:

    sudo nano /etc/nginx/sites-available/default
  2. In the location block for WebSocket connections, add the proxy_pass directive:

    location /websocket/ {
       proxy_pass http://your_websocket_server;
    }

    Replace "your_websocket_server" with the address of your WebSocket server.

Tip: Use SSL for WebSocket Connections

To secure your WebSocket connections, use SSL/TLS. Modify your proxy_pass directive to use HTTPS:

location /websocket/ {
    proxy_pass https://your_websocket_server;
}

This helps protect the data transmitted between the client and the server.

Header Modifications

To set up the headers for WebSocket communication:

  1. In the same location block, add these header modifications:

    location /websocket/ {
       proxy_pass http://your_websocket_server;
       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;
    }

    These headers help keep the original client information when passing through the proxy.

Connection Upgrade Handling

To manage WebSocket connection upgrades through NGINX:

  1. Add these directives to your location block:

    location /websocket/ {
       proxy_pass http://your_websocket_server;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
    }

    The 'Upgrade' and 'Connection' headers are needed for the WebSocket protocol handshake.

  2. Save the file and exit the editor.

  3. Test your NGINX configuration:

    sudo nginx -t
  4. If the test is successful, reload NGINX:

    sudo systemctl reload nginx

These settings allow NGINX to handle WebSocket connections, including the initial handshake and communication.

Fine-tuning NGINX for Optimal WebSocket Performance

Timeout Settings

To handle long-living WebSocket connections, adjust the proxy_read_timeout setting in your NGINX configuration:

  1. Open your NGINX configuration file:

    sudo nano /etc/nginx/sites-available/default
  2. In your WebSocket location block, add or modify the proxy_read_timeout directive:

    location /websocket/ {
       proxy_pass http://your_websocket_server;
       proxy_read_timeout 3600s;
    }

    This sets the read timeout to 1 hour (3600 seconds), which is good for most WebSocket applications.

  3. You can also add proxy_send_timeout:

    proxy_send_timeout 3600s;

Buffer Size Optimization

To optimize buffer sizes for WebSocket traffic:

  1. In the same location block, add these directives:

    location /websocket/ {
       proxy_pass http://your_websocket_server;
       proxy_buffering off;
       proxy_buffer_size 4k;
       proxy_buffers 4 4k;
    }
    • proxy_buffering off: This turns off buffering of responses from the proxied server, which is often good for WebSocket connections.
    • proxy_buffer_size 4k: This sets the size of the buffer for reading the first part of the response from the proxied server.
    • proxy_buffers 4 4k: This sets the number and size of buffers for a single connection.
  2. If you expect large messages, you may need to increase these values:

    proxy_buffer_size 8k;
    proxy_buffers 8 8k;
  3. Save the file and exit the editor.

  4. Test your NGINX configuration:

    sudo nginx -t
  5. If the test passes, reload NGINX:

    sudo systemctl reload nginx

Testing and Troubleshooting

Verifying Secure WebSocket Connections

To test your WebSocket proxy setup:

  1. Use online WebSocket testing tools:

    • Visit websocket.org/echo.html
    • Enter your WebSocket URL (wss://yourdomain.com/websocket)
    • Click "Connect" and check if the connection works
  2. Use command-line tools:

    • Install wscat: npm install -g wscat
    • Connect to your WebSocket server: wscat -c wss://yourdomain.com/websocket
    • Send a message and check if you get a response
  3. Check NGINX logs:

    • View error logs: sudo tail -f /var/log/nginx/error.log
    • View access logs: sudo tail -f /var/log/nginx/access.log
  4. Use browser developer tools:

    • Open your web application
    • Go to the Network tab
    • Filter for WebSocket connections
    • Check if the connection is set up and messages are sent/received

Tip: Use Browser Console for WebSocket Debugging

Open your browser's console (usually F12 or Ctrl+Shift+I) and add the following code to monitor WebSocket events:

var ws = new WebSocket("wss://yourdomain.com/websocket");
ws.onopen = function() { console.log("WebSocket connected"); };
ws.onmessage = function(evt) { console.log("Received: " + evt.data); };
ws.onclose = function() { console.log("WebSocket closed"); };
ws.onerror = function(err) { console.log("WebSocket error: ", err); };

This will log WebSocket events in real-time, helping you debug connection issues.

Common Issues and Solutions

  1. Connection refused:

    • Check if your WebSocket server is running
    • Verify the proxy_pass directive points to the right address and port
  2. SSL certificate errors:

    • Make sure your SSL certificates are valid and not expired
    • Check if the certificate files are correctly referenced in the NGINX configuration
  3. WebSocket handshake failing:

    • Verify that the Upgrade and Connection headers are set correctly
    • Make sure proxy_http_version is set to 1.1
  4. Timeouts during long-running connections:

    • Increase proxy_read_timeout and proxy_send_timeout values
    • Implement a ping-pong mechanism in your WebSocket application
  5. Large messages not being transmitted:

    • Increase client_max_body_size in your NGINX configuration
    • Adjust proxy_buffer_size and proxy_buffers if needed
  6. Cross-Origin Resource Sharing (CORS) issues:

    • Add headers to allow CORS:
      add_header 'Access-Control-Allow-Origin' '*';
      add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
      add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
  7. HTTP/2 compatibility issues:

    • If using HTTP/2, add proxy_set_header Connection ""; to your location block