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
serverblock 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 -tpassing
Why HTTPS Matters
| Without HTTPS | With HTTPS |
|---|---|
| Traffic readable on the network | Encrypted in transit |
| Browser may show “Not secure” | Padlock in address bar |
| Cookies easier to intercept | Secure cookies meaningful |
| HTTP/2 often unavailable | HTTP/2 common on 443 |
Apps behind Nginx can still speak plain HTTP on 127.0.0.1—only the edge needs certificates.
Install Certbot
sudo apt update
sudo apt install -y certbot python3-certbot-nginxVerify:
certbot --versionObtain a Certificate (Nginx Plugin)
Ensure your domain already resolves to this server:
dig +short example.com
curl -I http://example.com/Run Certbot:
sudo certbot --nginx -d example.com -d www.example.comCode explanation:
--nginx— Certbot edits Nginx config and reloads- Follow prompts: email, terms, optional redirect HTTP → HTTPS (choose redirect for production)
Certbot stores files under:
/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pemWarning
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:
curl -I https://example.com/Manual HTTP → HTTPS Redirect (Before Certbot)
If you configure SSL yourself first:
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:
sudo systemctl status certbot.timer
sudo certbot renew --dry-runCode 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:
sudo certbot renew
sudo nginx -t && sudo systemctl reload nginxStaging / Testing (Avoid Rate Limits)
First-time experiments with broken DNS hit Let's Encrypt rate limits. Use staging:
sudo certbot --nginx -d example.com --stagingRemove --staging for real certificates after config is correct.
Common Certbot Problems
| Problem | Fix |
|---|---|
| DNS not pointing to server | Fix A record; wait for TTL |
| Port 80 blocked | Open firewall; Certbot HTTP-01 needs it |
Wrong server_name | Nginx must have matching server_name on port 80 |
| Nginx config invalid | Fix nginx -t before running Certbot |
Check Certbot logs:
sudo less /var/log/letsencrypt/letsencrypt.logFAQ
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.