How to Set Up Nginx Reverse Proxy with SSL (Let's Encrypt)
What You’ll Need
- n8n Cloud or self-hosted n8n (optional, for API monitoring)
- Hetzner VPS or Contabo VPS for your server
- Namecheap for domain registration
- DigitalOcean as an alternative hosting option
- SSH access to your Linux server (Ubuntu 20.04 or later preferred)
- A domain name pointing to your server’s IP address
- Basic command-line knowledge
Table of Contents
- Understanding Nginx Reverse Proxy Architecture
- Installing Nginx and Certbot
- Configuring Your First Reverse Proxy
- Setting Up SSL with Let’s Encrypt
- Securing Your Configuration
- Getting Started
Understanding Nginx Reverse Proxy Architecture
I’ve been managing production servers for years, and one thing I learned early: a reverse proxy is your infrastructure’s best friend. Unlike a forward proxy (which shields clients), a reverse proxy sits between your internet-facing traffic and your internal applications. This setup gives you load balancing, SSL termination, security hardening, and the ability to run multiple services behind a single domain.
Here’s why this matters: if you’re running multiple backend services—say, a Node.js API on port 3000 and a Python Flask app on port 5000—a reverse proxy lets clients connect to both via api.example.com and app.example.com without exposing internal ports. Add Let’s Encrypt SSL, and you’ve got enterprise-grade encryption for free.
This is especially useful if you’re building automation workflows. For example, if you’re running a self-hosted n8n Cloud instance or managing automation workflows that replace expensive SaaS tools , you’ll want that traffic encrypted end-to-end.
Installing Nginx and Certbot
First, SSH into your server. I’m assuming you’ve already provisioned a Hetzner VPS or Contabo VPS with Ubuntu 20.04 or later. Your domain should already be pointing to your server’s IP address (if not, update your DNS records at Namecheap ).
Update your package manager and install Nginx:
sudo apt update
sudo apt upgrade -y
sudo apt install -y nginx
Start Nginx and enable it to run on boot:
sudo systemctl start nginx
sudo systemctl enable nginx
Check that Nginx is running:
sudo systemctl status nginx
You should see active (running) in the output. Now install Certbot, the Let’s Encrypt client, and the Nginx plugin:
sudo apt install -y certbot python3-certbot-nginx
Verify the installation:
certbot --version
Great. Now we’re ready to configure.
Configuring Your First Reverse Proxy
Let’s say you have a backend service running on localhost:3000 (maybe a Node.js app, a Python service, or anything HTTP). You want to expose it securely at api.example.com.
Open the Nginx configuration directory:
sudo nano /etc/nginx/sites-available/default
Replace the entire file with this configuration:
upstream backend_service {
server localhost:3000;
keepalive 32;
}
server {
listen 80;
listen [::]:80;
server_name api.example.com;
location / {
proxy_pass http://backend_service;
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;
proxy_redirect off;
}
}
Let me break this down:
- upstream backend_service: Defines where traffic goes. Point this to your actual backend port.
- listen 80: Listen on HTTP (Certbot will upgrade this to HTTPS).
- server_name api.example.com: Your domain. Change this to your actual domain.
- proxy_pass: Routes requests to your backend service.
- proxy_set_header directives: Preserve client information (IP, protocol, domain) so your backend app sees the real client.
Test the configuration syntax:
sudo nginx -t
If you see syntax is ok, reload Nginx:
sudo systemctl reload nginx
If you visit http://api.example.com in your browser right now, it should proxy to your backend service on port 3000. The connection is unencrypted, though—that’s next.
💡 Fast-Track Your Project: Don’t want to configure this yourself? I build custom n8n pipelines and bots. Message me with code SYS3-HUGO.
Setting Up SSL with Let’s Encrypt
This is where the magic happens. Certbot automates the entire SSL setup, including renewal. Run Certbot with the Nginx plugin:
sudo certbot --nginx -d api.example.com
Certbot will ask a few questions:
- Enter your email address (for renewal reminders).
- Accept the Let’s Encrypt terms of service.
- Optionally share your email with the EFF (their choice).
Choose “2” when asked about redirecting HTTP to HTTPS:
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, unless you have a specific reason otherwise.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Certbot automatically modifies your Nginx config to enable HTTPS and redirect HTTP traffic. Here’s what your config now looks like:
sudo cat /etc/nginx/sites-available/default
You’ll see something like this (Certbot added the HTTPS server block and modified the HTTP block):
upstream backend_service {
server localhost:3000;
keepalive 32;
}
server {
listen 80;
listen [::]:80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://backend_service;
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;
proxy_redirect off;
}
}
Test the syntax:
sudo nginx -t
Reload Nginx:
sudo systemctl reload nginx
Now visit https://api.example.com. Your connection is encrypted with a valid SSL certificate—and it’s completely free. Your certificate is valid for 90 days. Certbot automatically renews it before expiration via a system timer.
Verify the renewal timer is active:
sudo systemctl status certbot.timer
You should see active in the output.
Securing Your Configuration
A reverse proxy without proper security is like leaving your front door unlocked. Let’s harden the setup.
1. Add Security Headers
Edit your Nginx config:
sudo nano /etc/nginx/sites-available/default
Add these directives inside the HTTPS server block (after ssl_dhparam):
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
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;
These headers prevent clickjacking, MIME sniffing, and enforce HTTPS everywhere.
2. Limit Request Rate
If you’re concerned about DDoS or brute-force attacks, add rate limiting. Insert this outside any server block (near the top of the file):
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
Then add this inside the HTTPS server block:
limit_req zone=general burst=20 nodelay;
3. Restrict File Access
Add this inside the HTTPS server block to block sensitive files:
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ ~$ {
deny all;
access_log off;
log_not_found off;
}
4. Enable Gzip Compression
Outside any server block, add:
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss;
gzip_disable "MSIE [1-6]\.";
Test and reload:
sudo nginx -t
sudo systemctl reload nginx
Advanced: Multiple Backend Services
If you’re running multiple services (say, an API and a web dashboard), create separate upstream blocks and location directives. Here’s an example:
sudo nano /etc/nginx/sites-available/default
upstream api_backend {
server localhost:3000;
keepalive 32;
}
upstream app_backend {
server localhost:5000;
keepalive 32;
}
server {
listen 80;
listen [::]:80;
server_name api.example.com app.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live
Want to automate this yourself?
Start with n8n Cloud (free tier available) or self-host on a Hetzner VPS for full control.
📬 Get Weekly Automation Tips
One email per week with tutorials, tools, and workflows. No spam, unsubscribe anytime.