Nginx reverse proxy for Node.js | VPS Tutorial | ServerPoint Skip to main content
Linux

How to set up Nginx as a reverse proxy for Node.js

By ServerPoint's Team January 15, 2026

Why use Nginx in front of Node.js?

Running Node.js directly on port 80 or 443 works, but it’s not ideal for production. Nginx as a reverse proxy gives you:

  • SSL/TLS termination - Handle HTTPS at the Nginx level
  • Static file serving - Nginx serves static files faster than Node.js
  • Load balancing - Distribute traffic across multiple Node.js instances
  • Security - Hide your Node.js port from the internet
  • Caching - Cache responses to reduce load on your application

Prerequisites

Before starting, you need:

  • A VPS with Node.js installed
  • A Node.js application running (we’ll assume port 3000)
  • A domain name pointing to your server (for SSL)
  • SSH access with root or sudo privileges

Step 1: Install Nginx

Update your packages and install Nginx:

sudo apt update
sudo apt install -y nginx

Start Nginx and enable it to run on boot:

sudo systemctl start nginx
sudo systemctl enable nginx

Verify it’s running:

sudo systemctl status nginx

Step 2: Configure the reverse proxy

Create a new Nginx configuration file for your site:

sudo nano /etc/nginx/sites-available/myapp

Add this configuration (replace yourdomain.com with your actual domain):

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        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;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Key settings explained:

  • proxy_pass - Forward requests to your Node.js app on port 3000
  • proxy_http_version 1.1 - Required for WebSocket support
  • Upgrade and Connection headers - Enable WebSocket connections
  • X-Real-IP and X-Forwarded-For - Pass the real client IP to Node.js
  • X-Forwarded-Proto - Tell Node.js if the original request was HTTPS

Step 3: Enable the site

Create a symbolic link to enable the site:

sudo ln -s /etc/nginx/sites-available/myapp \
  /etc/nginx/sites-enabled/

Remove the default site (optional):

sudo rm /etc/nginx/sites-enabled/default

Test the configuration:

sudo nginx -t

If the test passes, reload Nginx:

sudo systemctl reload nginx

Step 4: Add SSL with Let’s Encrypt

Install Certbot for free SSL certificates:

sudo apt install -y certbot python3-certbot-nginx

Get a certificate for your domain:

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot will:

  1. Verify you own the domain
  2. Obtain an SSL certificate
  3. Automatically configure Nginx for HTTPS
  4. Set up auto-renewal

Your site now works on HTTPS!

Step 5: Configure your firewall

In your ServerPoint’s Client Portal, update your firewall rules:

  • Port 80 (HTTP) - Open for Let’s Encrypt verification and HTTP-to-HTTPS redirect
  • Port 443 (HTTPS) - Open for secure traffic
  • Port 3000 - Keep closed! Nginx handles external traffic now
  • Port 22 (SSH) - Restrict to your IP or keep closed in Global firewall

Serving static files with Nginx

For better performance, let Nginx serve static files directly instead of proxying them to Node.js.

Update your configuration:

sudo nano /etc/nginx/sites-available/myapp

Add a location block for static files before the main location block:

server {
    listen 443 ssl;
    server_name yourdomain.com www.yourdomain.com;

    # SSL configuration added by Certbot
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    # Serve static files directly
    location /static/ {
        alias /home/youruser/myapp/public/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Proxy everything else to Node.js
    location / {
        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;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Adjust the alias path to match where your static files are located.

Testing your setup

After reloading Nginx, test your application:

  1. Visit https://yourdomain.com - should show your Node.js app
  2. Check the browser’s security icon - should show a valid SSL certificate
  3. Test WebSocket connections if your app uses them

Troubleshooting

502 Bad Gateway

  • Your Node.js app isn’t running
  • Check with: sudo systemctl status myapp (if using systemd)
  • Or check if the port is listening: ss -tlnp | grep 3000

504 Gateway Timeout

  • Node.js is taking too long to respond
  • Add timeout settings to the location block:
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

SSL certificate errors

  • Make sure your domain points to your server’s IP
  • Run sudo certbot renew --dry-run to test renewal

Next steps


Explore our VPS plans for production-ready servers.