HTTPS and Let's Encrypt

Introduction

Production sites need HTTPS—encrypted traffic, trusted identity, and no browser warnings. Nginx terminates TLS on port 443 and can redirect HTTP to HTTPS automatically. This chapter uses Let's Encrypt and Certbot on Ubuntu/Debian—the fastest path to free certificates for a real domain.

Prerequisites

  • Reverse Proxy Basics or a working server block on port 80
  • A domain name whose DNS A record points to your server’s public IP
  • Ports 80 and 443 open in firewall and cloud security group
  • Nginx installed and sudo nginx -t passing

Why HTTPS Matters

Without HTTPSWith HTTPS
Traffic readable on the networkEncrypted in transit
Browser may show “Not secure”Padlock in address bar
Cookies easier to interceptSecure cookies meaningful
HTTP/2 often unavailableHTTP/2 common on 443

Apps behind Nginx can still speak plain HTTP on 127.0.0.1—only the edge needs certificates.

Install Certbot

bash
sudo apt update
sudo apt install -y certbot python3-certbot-nginx

Verify:

bash
certbot --version

Obtain a Certificate (Nginx Plugin)

Ensure your domain already resolves to this server:

bash
dig +short example.com
curl -I http://example.com/

Run Certbot:

bash
sudo certbot --nginx -d example.com -d www.example.com

Code explanation:

  • --nginx — Certbot edits Nginx config and reloads
  • Follow prompts: email, terms, optional redirect HTTP → HTTPS (choose redirect for production)

Certbot stores files under:

text
/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem

Warning

Do Not Commit Private Keys

Never copy privkey.pem to Git or paste in chat. Backups belong in secure secret storage only.

What Certbot Adds to Nginx

Typical result (simplified):

Code explanation:

  • 443 server — TLS + your app or static config
  • 80 server — permanent redirect to HTTPS
  • options-ssl-nginx.conf — Mozilla-style secure defaults from Certbot

Test:

bash
curl -I https://example.com/

Manual HTTP → HTTPS Redirect (Before Certbot)

If you configure SSL yourself first:

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

Certbot can merge this when you choose redirect during setup.

Renewal

Let's Encrypt certificates expire in 90 days. Certbot installs a timer:

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

Code explanation:

  • renew --dry-run — test renewal without changing certs
  • Production renewals run automatically; ensure port 80 stays reachable for HTTP-01 challenges (default)

Manual renewal if needed:

bash
sudo certbot renew
sudo nginx -t && sudo systemctl reload nginx

Staging / Testing (Avoid Rate Limits)

First-time experiments with broken DNS hit Let's Encrypt rate limits. Use staging:

bash
sudo certbot --nginx -d example.com --staging

Remove --staging for real certificates after config is correct.

Common Certbot Problems

ProblemFix
DNS not pointing to serverFix A record; wait for TTL
Port 80 blockedOpen firewall; Certbot HTTP-01 needs it
Wrong server_nameNginx must have matching server_name on port 80
Nginx config invalidFix nginx -t before running Certbot

Check Certbot logs:

bash
sudo less /var/log/letsencrypt/letsencrypt.log

FAQ

Can I use HTTPS without a domain?

Let's Encrypt requires a public domain name—not bare IP for standard issuance. Self-signed certs work for local dev only.

Wildcard certificates?

Require DNS-01 challenge (certbot certonly --manual or DNS plugin)—not covered in basic --nginx flow.

Certbot vs manual ssl_certificate?

Certbot automates issuance and renewal. Manual paths suit custom CA or imported corporate certs.

HTTPS to upstream localhost?

Usually unnecessary on same machine. Edge TLS + HTTP to 127.0.0.1:8080 is standard.

Multiple domains on one server?

One Certbot run with -d a.com -d b.com or separate certs per server block.

What if renewal fails?

Sites may show cert expiry warnings—monitor certbot.timer and logs; fix port 80/DNS before expiry.