Nginx as a Reverse Proxy:
Complete Setup with Let's Encrypt SSL

Nginx's reverse proxy capability is what makes it the backbone of modern web infrastructure. Whether you're serving a Node.js API, a PHP backend, or a Dockerized service, this guide covers every configuration detail you'll actually need - including free TLS from Let's Encrypt.

Installation

On Debian and Ubuntu, install Nginx from the official repositories and verify it starts cleanly:

apt update && apt install nginx
systemctl enable --now nginx

# Check that Nginx is listening
ss -tlnp | grep ':80'

# Verify the default page loads
curl -I http://localhost

Nginx's configuration lives under /etc/nginx/. The main file is nginx.conf, and per-site configs go in /etc/nginx/sites-available/, then symlinked into sites-enabled/. Disable the default site: rm /etc/nginx/sites-enabled/default.

Basic Reverse Proxy Configuration

A reverse proxy configuration tells Nginx to forward incoming requests to a backend process - typically listening on a local port. Create /etc/nginx/sites-available/myapp:

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass         http://127.0.0.1:3000;
        proxy_http_version 1.1;

        # Forward the original host and IP to the backend
        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;

        # Required for WebSocket support
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout    60s;
        proxy_read_timeout    60s;
    }
}

Enable the site and test the configuration:

ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Free SSL with Let's Encrypt (Certbot)

Let's Encrypt issues free 90-day TLS certificates. Certbot handles both the issuance and renewal automatically. The Nginx plugin edits your config to add SSL directives and sets up an HTTP-to-HTTPS redirect.

apt install certbot python3-certbot-nginx

# Issue a certificate and auto-configure Nginx
certbot --nginx -d app.example.com

# Test automatic renewal
certbot renew --dry-run

Certbot installs a systemd timer that runs twice daily to check for expiring certificates. Your certificates live in /etc/letsencrypt/live/app.example.com/.

ℹ️
DNS must resolve before running Certbot. Let's Encrypt validates domain ownership by making an HTTP request to your server. Make sure your DNS A record points to the server's IP and has propagated before running the certbot command.

Proxying Multiple Services

A single Nginx server can proxy many different backends simultaneously. Use separate server blocks per domain, or path-based routing within a single block:

# Path-based routing to different backends
server {
    listen 443 ssl;
    server_name platform.example.com;

    # API service on port 4000
    location /api/ {
        proxy_pass http://127.0.0.1:4000/;
        proxy_set_header Host            $host;
        proxy_set_header X-Real-IP       $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # Frontend on port 3000
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host            $host;
        proxy_set_header X-Real-IP       $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Security Headers

Add these response headers to all sites. Put them in a shared snippet at /etc/nginx/snippets/security-headers.conf and include it in each server block:

# /etc/nginx/snippets/security-headers.conf
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options    "nosniff" always;
add_header X-Frame-Options           "SAMEORIGIN" always;
add_header Referrer-Policy           "strict-origin-when-cross-origin" always;
add_header Permissions-Policy        "geolocation=(), microphone=(), camera=()" always;

Include in your server block: include snippets/security-headers.conf;

Then verify with securityheaders.com - aim for an A+ rating.

Enable Gzip Compression

Enable gzip in /etc/nginx/nginx.conf inside the http block to significantly reduce response sizes for text-based content:

gzip            on;
gzip_vary       on;
gzip_proxied    any;
gzip_comp_level 6;
gzip_types
    text/plain text/css text/xml
    application/json application/javascript application/xml+rss
    application/atom+xml image/svg+xml;

After every configuration change, always run nginx -t before reloading. A failed nginx -t means the reload won't happen - your old config stays active - but it also means you won't accidentally take down a running service with a typo.

Services Technologies Process Blog Get in Touch