Setting Up Nginx as a Reverse Proxy on a VPS: A Step-by-Step Guide

Every VPS running a web application eventually needs a reverse proxy. Whether you are hosting multiple sites, running a Node.js API behind a domain, or adding TLS termination to an internal service, Nginx is the industry-standard tool for the job. It handles TLS termination, load balancing, caching, rate limiting, and static file serving — all while consuming minimal memory on a budget VPS.

This tutorial walks through setting up Nginx as a reverse proxy on a Linux VPS, from installation to production-ready configuration with TLS and security headers. If you have not yet chosen a provider, compare VPS hosting plans to find one with the resources you need.

Prerequisites

  • A Linux VPS (Ubuntu 22.04+ or Debian 12+ recommended)
  • Root or sudo access
  • A domain name pointing to your VPS IP address
  • A backend application running locally (e.g., on port 3000, 8080, or a Unix socket)

Step 1: Install Nginx

sudo apt update
sudo apt install -y nginx

# Verify the installation
sudo nginx -v
sudo systemctl status nginx

# Enable Nginx to start on boot
sudo systemctl enable nginx

Step 2: Configure a Basic Reverse Proxy

Create a new site configuration file for your domain:

sudo nano /etc/nginx/sites-available/yourdomain.com

Add the following configuration, which proxies all requests to a backend running on port 3000:

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

Enable the site and test:

sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Your backend application should now be accessible via http://yourdomain.com.

Step 3: Add TLS with Let’s Encrypt

Never serve a reverse proxy over plain HTTP in production. Use Certbot to obtain a free TLS certificate:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot automatically modifies your Nginx configuration to serve HTTPS on port 443 and redirect HTTP to HTTPS. The certificates renew automatically via a systemd timer:

sudo systemctl status certbot.timer

Step 4: Tune Proxy Buffers for Performance

Default proxy buffer sizes are conservative. For production workloads, especially proxying to slow backend APIs, increase them:

server {
    # ... existing server block ...

    proxy_buffering on;
    proxy_buffer_size 8k;
    proxy_buffers 8 8k;
    proxy_busy_buffers_size 16k;
    proxy_temp_file_write_size 16k;

    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;

    location / {
        # ... existing proxy config ...
    }
}

Adjust timeouts based on your application’s response time. For WebSocket-heavy apps, set proxy_read_timeout to 86400s (24 hours) to prevent mid-session disconnects.

Step 5: Add Security Headers

Add these headers inside your server block to harden the reverse proxy against common attacks:

server {
    # ... existing config ...

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Hide Nginx version
    server_tokens off;
}

Step 6: Proxy Multiple Backend Services

To proxy multiple services on the same VPS, use separate location blocks with different proxy_pass targets:

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

    # API backend
    location /api/ {
        proxy_pass http://127.0.0.1:3000/;
        # ... proxy headers ...
    }

    # Admin dashboard
    location /admin/ {
        proxy_pass http://127.0.0.1:4000/;
        # ... proxy headers ...
    }

    # Static files served directly by Nginx
    location /static/ {
        root /var/www/yourdomain.com;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

Note the trailing slash on proxy_passhttp://127.0.0.1:3000/ strips the /api prefix, while http://127.0.0.1:3000 (without trailing slash) passes the full path including /api.

Step 7: Rate Limiting and Access Control

Protect your backend from abuse with Nginx’s built-in rate limiting:

# In http block (nginx.conf or /etc/nginx/nginx.conf)
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

# In server block
location /api/ {
    limit_req zone=api burst=20 nodelay;
    proxy_pass http://127.0.0.1:3000/;
}

To restrict access by IP for admin-only routes:

location /admin/ {
    allow 192.168.1.100;
    allow 10.0.0.0/8;
    deny all;
    proxy_pass http://127.0.0.1:4000/;
}

Testing and Troubleshooting

After configuration, verify everything works:

# Check Nginx syntax
sudo nginx -t

# Reload if valid
sudo systemctl reload nginx

# Check access logs
sudo tail -f /var/log/nginx/access.log

# Check error logs
sudo tail -f /var/log/nginx/error.log

Common issues and fixes:

  • 502 Bad Gateway: Your backend is not running or not listening on the expected port. Restart the backend service.
  • Connection refused: The proxy_pass target is unreachable. Check that the backend binds to 127.0.0.1 (not 0.0.0.0).
  • SSL certificate errors: Run sudo certbot renew to refresh certificates.

Nginx as a reverse proxy is one of the most valuable skills for VPS administration. For more VPS hosting tips and provider comparisons, visit Virtual Servers VPS.

Leave a Reply