Configure NGINX SSL termination with Certbot for Let's Encrypt certificates

Intermediate 25 min May 23, 2026 30 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up NGINX with automatic SSL certificate management using Let's Encrypt and Certbot, including security headers and automated renewal for production-ready HTTPS termination.

Prerequisites

  • Domain name pointing to your server
  • Root or sudo access
  • Port 80 and 443 accessible

What this solves

SSL termination handles encrypted connections at your web server, decrypting HTTPS traffic before passing it to backend applications. This tutorial sets up NGINX with Let's Encrypt certificates through Certbot, giving you free, automatically renewing SSL certificates with proper security headers for production use.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions of NGINX and Certbot.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install NGINX web server

Install NGINX which will handle SSL termination and serve as your reverse proxy or web server.

sudo apt install -y nginx
sudo dnf install -y nginx

Install Certbot and NGINX plugin

Certbot automates Let's Encrypt certificate requests and renewals. The NGINX plugin handles automatic configuration updates.

sudo apt install -y certbot python3-certbot-nginx
sudo dnf install -y certbot python3-certbot-nginx

Enable and start NGINX

Enable NGINX to start automatically on boot and start the service now.

sudo systemctl enable --now nginx
sudo systemctl status nginx

Configure firewall for HTTP and HTTPS

Open ports 80 and 443 to allow web traffic. Port 80 is needed for Let's Encrypt validation and HTTP to HTTPS redirects.

sudo ufw allow 'Nginx Full'
sudo ufw reload
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Create basic NGINX configuration

Set up a basic server block for your domain. Replace example.com with your actual domain name.

server {
    listen 80;
    server_name example.com www.example.com;
    
    root /var/www/html;
    index index.html index.htm;
    
    location / {
        try_files $uri $uri/ =404;
    }
    
    # Let's Encrypt challenge location
    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }
}

Enable the site configuration

Create a symbolic link to enable the site and test the NGINX configuration syntax.

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Obtain SSL certificate with Certbot

Request a Let's Encrypt certificate for your domain. Certbot will automatically modify your NGINX configuration to include SSL settings.

sudo certbot --nginx -d example.com -d www.example.com
Note: Enter a valid email address when prompted. This is used for renewal notifications and security notices from Let's Encrypt.

Configure enhanced security headers

Add security headers to protect against common web vulnerabilities. Update your NGINX configuration with these headers.

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;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;
    
    root /var/www/html;
    index index.html index.htm;
    
    location / {
        try_files $uri $uri/ =404;
    }
}

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

Optimize SSL configuration

Configure additional SSL optimizations for better performance and security. Add these settings to your main NGINX configuration.

http {
    # SSL Session Cache
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    
    # SSL Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    
    # DNS resolver for SSL stapling
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
    
    # Existing configuration...
}

Test and reload configuration

Verify the NGINX configuration syntax and reload to apply all changes.

sudo nginx -t
sudo systemctl reload nginx

Set up automatic certificate renewal

Certbot installs a systemd timer for automatic renewals. Verify it's active and test the renewal process.

sudo systemctl status certbot.timer
sudo certbot renew --dry-run

Create renewal hook script

Create a hook script to reload NGINX after certificate renewal to ensure new certificates are used immediately.

#!/bin/bash
nginx -t && systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Configure SSL termination for backend applications

Set up reverse proxy configuration

Configure NGINX to terminate SSL and proxy requests to backend applications. This example proxies to a Node.js app running on port 3000.

upstream backend {
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s backup;
}

server {
    listen 443 ssl http2;
    server_name app.example.com;
    
    ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    
    # Proxy configuration
    location / {
        proxy_pass http://backend;
        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;
        
        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
    
    # Health check endpoint
    location /health {
        access_log off;
        proxy_pass http://backend/health;
        proxy_set_header Host $host;
    }
}

server {
    listen 80;
    server_name app.example.com;
    return 301 https://$server_name$request_uri;
}

Enable backend application site

Enable the backend application configuration and obtain SSL certificates for it.

sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo certbot --nginx -d app.example.com
sudo nginx -t
sudo systemctl reload nginx

Set up monitoring and logging

Configure access and error logging

Set up detailed logging for SSL termination monitoring and troubleshooting.

http {
    # Log format with SSL information
    log_format ssl_combined '$remote_addr - $remote_user [$time_local] '
                           '"$request" $status $body_bytes_sent '
                           '"$http_referer" "$http_user_agent" '
                           'ssl_protocol=$ssl_protocol ssl_cipher=$ssl_cipher';
    
    access_log /var/log/nginx/access.log ssl_combined;
    error_log /var/log/nginx/error.log warn;
    
    # Existing configuration...
}

Set up log rotation

Configure logrotate to manage NGINX log files and prevent disk space issues.

/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 644 www-data adm
    postrotate
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 cat /var/run/nginx.pid
        fi
    endscript
}

Verify your setup

Test your SSL termination configuration with these verification commands.

# Check NGINX status and configuration
sudo systemctl status nginx
sudo nginx -t

Verify certificate details

sudo certbot certificates

Test SSL configuration

curl -I https://example.com ssl-cert-check -s example.com -p 443

Check renewal timer

sudo systemctl list-timers certbot.timer

Test HTTP to HTTPS redirect

curl -I http://example.com

You can also use online SSL testing tools to verify your configuration:

# Test with OpenSSL
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

Check SSL Labs rating (replace with your domain)

echo "Test your SSL configuration at: https://www.ssllabs.com/ssltest/analyze.html?d=example.com"

Common issues

SymptomCauseFix
Certificate request failsDomain not pointing to serverVerify DNS A record points to server IP
502 Bad Gateway errorsBackend application downCheck backend service status and proxy_pass configuration
SSL certificate not foundCertbot failed to modify configRun sudo certbot --nginx -d yourdomain.com again
Mixed content warningsHTTP resources on HTTPS pageUpdate all resource URLs to HTTPS or relative paths
Certificate renewal failsWebroot path inaccessibleVerify /.well-known/acme-challenge/ location in NGINX config
HSTS warnings in browserPrevious HTTP access cachedClear browser cache or wait for HSTS max-age expiry

Next steps

Running this in production?

Want this handled for you? Setting this up once is straightforward. Keeping it patched, monitored, backed up and performant across environments is the harder part. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.