
How to Configure Nginx as a Reverse Proxy with SSL/TLS Let us Encrypt
When deploying web applications to production, running your application server (such as Node.js, Go, or Python) directly exposed to the public internet is a security and performance bottleneck. Application servers are not designed to handle massive concurrent requests, slow clients, or SSL/TLS handshakes efficiently.
The standard architecture is placing Nginx in front of your application as a Reverse Proxy. Nginx intercepts incoming public traffic, acts as a security buffer, serves static assets directly, and forwards clean requests to your application server.
In this guide, we will configure Nginx as a reverse proxy, set up proxy headers, and secure it with free, auto-renewing SSL certificates using Let us Encrypt.
Why Use Nginx as a Reverse Proxy?
- SSL/TLS Termination: Nginx handles the CPU-heavy encryption/decryption layer, allowing your backend application to run over plain HTTP on localhost, saving processing resources.
- Security Shielding: Nginx hides the identity and structure of your backend server, shielding it from direct port attacks.
- Static File Caching: Nginx can serve static images, CSS, and JS files directly from disk without touching your backend server, freeing up your app thread.
- Load Balancing: If your app grows, Nginx can distribute incoming traffic across multiple instances of your application.
Step 1: Install Nginx
On Debian or Ubuntu servers:
sudo apt update
sudo apt install nginxOnce installed, Nginx starts automatically. Verify that it is running:
sudo systemctl status nginxStep 2: Configure the Reverse Proxy
Create a new configuration file for your domain in the sites-available directory:
sudo nano /etc/nginx/sites-available/example.comPaste the following server configuration. This setup assumes your backend application runs on localhost port 3000:
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Enable WebSockets support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
# Forward original client IP and host details
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;
# Disable response buffering for streaming APIs (optional)
proxy_buffering off;
}
# Static files caching block
location /static/ {
alias /var/www/myapp/static/;
expires 30d;
add_header Cache-Control "public, no-transform";
}
}Enable this site by creating a symbolic link to the sites-enabled directory:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/Validate your Nginx configuration syntax:
sudo nginx -tIf it reports success, restart Nginx to apply changes:
sudo systemctl restart nginxStep 3: Secure the Proxy with Let us Encrypt SSL
We will use Certbot, the official client tool for Let us Encrypt, to acquire a free SSL/TLS certificate.
First, install Certbot and its Nginx plugin:
sudo apt install certbot python3-certbot-nginxRun Certbot to request a certificate. Certbot automatically inspects your Nginx configurations, requests the certificate for your domain, and modifies your Nginx configuration to enable SSL:
sudo certbot --nginx -d example.com -d www.example.comDuring the prompt, select the option to automatically redirect all HTTP traffic to HTTPS.
Certbot configures a cron job to automatically renew your certificates before they expire, ensuring your site remains secure indefinitely.
Testing Auto-Renewal
To test the automated renewal process without making actual certificate requests, run:
sudo certbot renew --dry-runIf the dry run succeeds, your setup is complete. Nginx will automatically reload renewed certificates in the background.
Conclusion
Placing Nginx in front of your backend application server is a production deployment best practice. By offloading SSL termination, handling WebSocket upgrades, and caching static resources, Nginx improves both the security and speed of your backend services, allowing your code to focus entirely on executing dynamic business logic.