Let's Encrypt
02 / 02

Nginx Config, Wildcards & Auto-renewal

Let's Encrypt: Nginx Config, Wildcards & Renewal

Nginx Configuration

# /etc/nginx/sites-available/example.com
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name example.com www.example.com;

    # Let's Encrypt certificates
    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;   # generated by certbot
    ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;         # generated by certbot

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    root /var/www/example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    # For reverse proxy (Node.js, Rails, etc.)
    location /api/ {
        proxy_pass http://localhost: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 site
# sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# sudo nginx -t && sudo systemctl reload nginx

Wildcard Certificates with DNS-01

# Wildcard requires DNS-01 challenge (HTTP-01 can't validate *.domain.com)
# Use a DNS provider plugin for automation

# Cloudflare DNS plugin
pip install certbot-dns-cloudflare
# or: sudo snap install certbot-dns-cloudflare

# Create Cloudflare credentials file
cat > /etc/letsencrypt/cloudflare.ini << 'EOF'
dns_cloudflare_email = your@email.com
dns_cloudflare_api_key = your_global_api_key
# Or use API token (more secure):
# dns_cloudflare_api_token = your_api_token
EOF
chmod 600 /etc/letsencrypt/cloudflare.ini

# Issue wildcard certificate
sudo certbot certonly   --dns-cloudflare   --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini   -d example.com   -d '*.example.com'

# Other DNS plugins: certbot-dns-route53, certbot-dns-digitalocean,
#                   certbot-dns-google, certbot-dns-linode

# Manual DNS-01 (when no plugin available)
sudo certbot certonly --manual --preferred-challenges dns   -d '*.example.com'
# Follow prompts: add TXT record _acme-challenge.example.com
# Verify record propagated before pressing Enter:
# dig TXT _acme-challenge.example.com @8.8.8.8

Auto-renewal

# Certbot installs a systemd timer or cron job automatically
# Verify it's active
sudo systemctl status certbot.timer
sudo systemctl list-timers | grep certbot

# Or check cron
sudo cat /etc/cron.d/certbot

# Test renewal (dry run — doesn't actually renew)
sudo certbot renew --dry-run

# Force renewal (even if cert isn't expiring soon)
sudo certbot renew --force-renewal

# Renew specific domain only
sudo certbot renew --cert-name example.com

# Hook: reload nginx after renewal
sudo certbot renew   --deploy-hook "systemctl reload nginx"

# Or add to /etc/letsencrypt/renewal/example.com.conf:
# [renewalparams]
# post_hook = systemctl reload nginx

# List all certificates and expiry dates
sudo certbot certificates

Docker & Cloud Alternatives

# Traefik (auto-TLS for Docker/Kubernetes)
# docker-compose.yml
# traefik:
#   image: traefik:v3
#   command:
#     - --providers.docker=true
#     - --entrypoints.web.address=:80
#     - --entrypoints.websecure.address=:443
#     - --certificatesresolvers.le.acme.tlschallenge=true
#     - --certificatesresolvers.le.acme.email=admin@example.com
#     - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
#   volumes:
#     - /var/run/docker.sock:/var/run/docker.sock
#     - ./letsencrypt:/letsencrypt
#   ports: ["80:80", "443:443"]

# Caddy (auto-TLS built-in — simplest option)
# Caddyfile:
# example.com {
#     reverse_proxy localhost:3000
# }
# Caddy handles cert issuance and renewal automatically

# Kubernetes cert-manager
# kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
# Then create Issuer + Certificate resources

# Cloud-managed (no Certbot needed)
# - AWS ACM (free, auto-renew, integrates with ALB/CloudFront)
# - GCP Certificate Manager (free, auto-renew)
# - Azure App Service Managed Certificates (free)
  • 90-day certificate lifetime forces automation — manual renewal would be unsustainable.

  • Always use --staging for testing to avoid rate limits (5 failures/hour, 50 certs/domain/week).

  • Monitor certificate expiry: set up alerts at 30 days (UptimeRobot, Datadog, StatusCake — many check TLS expiry for free).

  • Backup /etc/letsencrypt/ — contains all certificates and keys. Losing private keys requires re-issuance.

  • Prefer ECDSA keys: certbot --key-type ecdsa uses smaller, faster ECDSA instead of RSA 2048.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free