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_pass — http://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(not0.0.0.0). - SSL certificate errors: Run
sudo certbot renewto 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
You must be logged in to post a comment.