Set up Nginx as a reverse proxy with free Let's Encrypt SSL certificates for any web application — Node.js, Python, Go, or Docker containers.
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:
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'
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.
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:
Verify auto-renewal works:
sudo certbot renew --dry-run
Certificates auto-renew every 90 days. You never need to touch this again.
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 conversionsRoute 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.