How to Setup Nginx Reverse Proxy for Node.js on VPS (2026)

Published

How to Setup Nginx Reverse Proxy for Node.js on VPS (2026)

To setup an Nginx reverse proxy for Node.js on a VPS, install Nginx, create a server block configuration, and use the proxy_pass directive to route traffic from port 80 (HTTP) or 443 (HTTPS) to your local Node.js port (e.g., 3000). Finally, secure the connection with free SSL using Certbot.

When you deploy a Node.js application to a VPS, it typically runs on a high port like 3000 or 8080. While you could configure Node.js to run directly on port 80 (HTTP), it requires root privileges and is highly discouraged in production environments.

The industry standard approach is to use Nginx as a Reverse Proxy. Nginx sits at the edge of your server, handles all incoming internet traffic, manages SSL certificates, and quietly forwards valid requests to your internal Node.js application.

This step-by-step guide will show you how to configure an Nginx reverse proxy for a Node.js app on a VPS in 2026. For a complete overview of the full hosting stack, check out our Node.js VPS Hosting Guide.

Why Use an Nginx Reverse Proxy with Node.js?

Before diving into the setup, it’s important to understand why this extra layer exists:

  • Security: Your Node.js app is completely hidden from the public internet. It binds to localhost:3000, meaning it cannot be directly accessed or exploited from the outside. Only Nginx communicates with it.
  • SSL Termination: Handling HTTPS inside Node.js is cumbersome. Nginx handles the SSL certificates efficiently, offloading cryptographic work from your single-threaded Node.js event loop.
  • Serving Static Assets: Node.js (via Express or Fastify) is relatively slow at serving static files (images, CSS, JS). Nginx is optimized specifically for this task and serves them instantly.
  • Load Balancing: If you are running multiple instances of your app (e.g., using PM2), Nginx can act as a load balancer to distribute traffic evenly.

Step 1: Install Nginx on Your VPS (Ubuntu/Debian)

Connect to your VPS via SSH. First, ensure your package lists are up to date, and then install Nginx.

sudo apt update
sudo apt install nginx -y

Once installed, Nginx will start automatically. If you visit your server’s public IP address in your browser (e.g., http://YOUR_SERVER_IP), you should see the default “Welcome to nginx!” page.

Step 2: Keep Node.js Running (PM2 Integration)

Before configuring Nginx to point to your app, your app must actually be running in the background. If you just run node server.js and close your SSH terminal, the app will stop.

You must use a process manager like PM2.

npm install -g pm2
pm2 start server.js --name my-node-app

Make sure your app is running locally, typically on port 3000. You can test this locally on the server:

curl http://localhost:3000

(If this returns HTML or JSON from your app, you are ready for the next step).

For detailed PM2 configurations, read our guide on the Best VPS setups for PM2 and Node.js.

Step 3: Configure the Reverse Proxy Block

Now we tell Nginx to intercept traffic on port 80 and forward it to port 3000.

Create a new Nginx server block configuration file for your domain:

sudo nano /etc/nginx/sites-available/my-node-app

Paste the following configuration. Be sure to replace yourdomain.com with your actual domain name, and 3000 with your Node.js port.

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

    location / {
        proxy_pass http://localhost:3000;
        
        # Standard Proxy Headers
        proxy_http_version 1.1;
        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 Support (Required for Next.js, Socket.io, etc)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_cache_bypass $http_upgrade;
    }
}

Essential Nginx Headers Explained

  • X-Real-IP: Without this, your Node.js app will think every user has the IP address 127.0.0.1 (since the request comes from the local Nginx proxy). This header passes the real user IP.
  • Upgrade & Connection: These headers are absolutely crucial in 2026. Without them, WebSockets (used by Socket.io, Next.js Hot Module Replacement, and many modern APIs) will fail to connect.

Save the file and exit (Ctrl+O, Enter, Ctrl+X).

Now, enable the configuration by creating a symlink to sites-enabled:

sudo ln -s /etc/nginx/sites-available/my-node-app /etc/nginx/sites-enabled/

Test the Nginx configuration for syntax errors:

sudo nginx -t

If it says syntax is ok, restart Nginx:

sudo systemctl restart nginx

If your DNS A-records are pointing to your VPS IP, you can now visit http://yourdomain.com and see your Node.js app!

Step 4: Secure with SSL (Certbot & Let’s Encrypt)

Serving traffic over HTTP is insecure. We will use Certbot to automatically obtain and configure a free SSL certificate from Let’s Encrypt.

Install Certbot and its Nginx plugin:

sudo apt install certbot python3-certbot-nginx -y

Run Certbot. It will automatically detect your Nginx configuration, issue the certificate, and modify your Nginx block to handle HTTPS (Port 443) and force HTTP to HTTPS redirects.

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

Follow the prompts. Once finished, visit https://yourdomain.com to verify your secure connection.

Step 5: Advanced Nginx Tweaks for Node.js (Optional)

If you are handling large file uploads or want to compress your API responses, you should add these tweaks to your location / block:

Increase Max Body Size: If your Node.js app allows image or video uploads, Nginx will block uploads larger than 1MB by default. Add this to allow up to 50MB uploads:

client_max_body_size 50M;

Enable Gzip Compression: Compress JSON and HTML responses before sending them to the client to dramatically improve performance.

gzip on;
gzip_proxied any;
gzip_types text/plain application/json text/css application/javascript;
gzip_min_length 1000;

Summary & Next Steps

You have successfully placed Nginx in front of your Node.js application. Nginx is now handling the public internet, terminating SSL connections, and safely proxying traffic to your isolated PM2 instances.

To take your production setup to the next level, learn how to automate this deployment process with our guide on Zero-Downtime Node.js Deployment with GitHub Actions.