What is a Reverse Proxy and Why Do You Need One?
A reverse proxy sits between the internet and your application. Instead of exposing your app directly on port 80/443, Nginx handles all incoming traffic and forwards it to your app running on a local port (like 3000 or 8000).
Benefits:
- SSL termination — Nginx handles HTTPS so your app doesn't need to
- Multiple apps, one server — route different domains to different apps
- Static file serving — Nginx serves static files 10x faster than your app
- Load balancing — distribute traffic across multiple app instances
- Security — hide your app's internal port, add rate limiting, block bad requests
Step 1 — Install Nginx
sudo apt update
sudo apt install nginx
# Verify it's running
sudo systemctl status nginx
# Test — visit http://your-server-ip in a browser
curl http://localhost
You should see the "Welcome to nginx" page. If UFW is enabled, allow HTTP/HTTPS:
sudo ufw allow 'Nginx Full'
Step 2 — Configure the Reverse Proxy
Create a configuration file for your domain:
sudo nano /etc/nginx/sites-available/myapp.conf
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
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/myapp.conf /etc/nginx/sites-enabled/
sudo nginx -t # Test configuration
sudo systemctl reload nginx
Make sure your DNS A record points to your server's IP address.
Step 3 — Add Free SSL with Let's Encrypt
Install Certbot and get a free SSL certificate:
sudo apt install certbot python3-certbot-nginx
# Get certificate (replace with your domain)
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot will:
- 1. Verify you own the domain
- 2. Generate the SSL certificate
- 3. Automatically modify your Nginx config to use HTTPS
- 4. Set up auto-renewal
Verify auto-renewal works:
sudo certbot renew --dry-run
Certificates auto-renew every 90 days. You never need to touch this again.
Step 4 — Optimize for Production
Add these optimizations to your Nginx config:
server {
listen 443 ssl http2;
server_name yourdomain.com;
# SSL config (added by Certbot)
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1000;
# File upload size (increase for file conversion tools)
client_max_body_size 200M;
# Timeouts for long-running conversions
proxy_connect_timeout 60s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
proxy_pass http://127.0.0.1:3000;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}
Key settings:
client_max_body_size 200M— essential for file upload applicationsproxy_read_timeout 120s— prevents timeouts on slow conversions- Static asset caching — reduces server load significantly
Multiple Apps on One Server
Route different domains to different applications:
# App 1: api.yourdomain.com -> port 8000
server {
server_name api.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
# ... same proxy headers
}
}
# App 2: yourdomain.com -> port 3000
server {
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
# ... same proxy headers
}
}
Run sudo certbot --nginx again for each new domain to add SSL. Nginx handles routing based on the Host header — all traffic comes in on port 443, and Nginx sends it to the right app.