Setting up Nginx as a Reverse Proxy with SSL
To use Nginx as a reverse proxy with SSL, you typically terminate HTTPS at Nginx (it handles TLS and certificates) and proxy plain HTTP traffic to your upstream application servers. This is done by defining HTTP and HTTPS server blocks, configuring certificates, and using proxy_pass plus standard X-Forwarded-* headers in your location blocks.[1][2][3][4]
Basic reverse proxy (HTTP)
- Edit a site config, for example
/etc/nginx/sites-available/example.conf.[4] - Add a reverse proxy
serverblock on port 80 pointing to your app (e.g.http://127.0.0.1:3000):[5][1]
server {
listen 80;
server_name example.com www.example.com;
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;
}
}
- Enable the site (
ln -sintosites-enabledon Debian/Ubuntu) and test/reload Nginx withnginx -tandsystemctl reload nginx.[4]
Obtaining an SSL certificate
- For public domains, a common approach is Let’s Encrypt with Certbot, using the webroot or Nginx plugin to issue a certificate for
example.com.[5] - Certificates are typically stored under
/etc/letsencrypt/live/example.com/withfullchain.pemandprivkey.pempaths used in Nginx.[5]
Example (Certbot webroot pattern):
sudo certbot certonly --webroot -w /var/www/example -d example.com -d www.example.com
HTTPS reverse proxy with SSL termination
- Add an HTTPS
serverblock listening on 443 with SSL enabled and pointing to your certificate and key.[2][3] - Proxy to the same upstream, usually over HTTP, while keeping the same
proxy_set_headerset so your app knows the original scheme and client IP.[2][4]
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Optional: basic SSL settings (use a hardened snippet in production)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
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 https;
}
}
Redirect HTTP to HTTPS
- Use a separate port 80
serverthat only redirects to HTTPS so all traffic is encrypted.[3][2]
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
Practical tips and best practices
- Keep a single reverse proxy host with multiple
server_nameblocks when serving many apps; use subdomains instead of long path prefixes to avoid cookie and routing issues.[6][4] - Always test configuration with
nginx -tbefore reloading, and useX-Forwarded-ProtoandX-Forwarded-Forconsistently so upstream apps (like Node, PHP, or frameworks) reconstruct original URLs and client IP correctly.[1][4]