Let's Encrypt issues free, trusted TLS certificates. Certbot requests them, configures Nginx for HTTPS and renews them before they expire.
Before you start
- Nginx serves your site over HTTP with a
server_namethat matches your domain. See Install Nginx. - Your domain's A record (and AAAA, if you use IPv6) points at the server. Check with
dig +short example.com. - Ports 80 and 443 are open in your firewall. Let's Encrypt connects on port 80 to verify you control the domain.
1. Install Certbot
Ubuntu and Debian:
sudo apt install -y certbot python3-certbot-nginxAlmaLinux and Rocky Linux get it from EPEL:
sudo dnf install -y epel-release
sudo dnf install -y certbot python3-certbot-nginx2. Request the certificate
List every name the certificate should cover with -d:
sudo certbot --nginx -d example.com -d www.example.comCertbot asks for an email address for expiry warnings and for you to accept the terms. If it asks whether to redirect HTTP to HTTPS, choose redirect. It then edits your server block and reloads Nginx.
Open https://example.com. The browser should show a padlock.
3. Make sure renewal is automatic
Certificates last 90 days. Certbot installs a timer that renews them well before expiry. Check it:
systemctl list-timers | grep -i certbotOn AlmaLinux and Rocky the timer may need enabling:
sudo systemctl enable --now certbot-renew.timerThen test a renewal without changing anything:
sudo certbot renew --dry-runAdd more names later
Run the same command with the full list of names, old and new. Certbot offers to expand the existing certificate:
sudo certbot --nginx -d example.com -d www.example.com -d shop.example.comWarning: Let's Encrypt limits how many certificates you can request for the same names in a short time. While you experiment, use --dry-run (or --staging) so failed attempts do not count against the real limit.
Troubleshooting
- Challenge failed / timeout: port 80 is blocked or the domain does not point at this server yet. Check
dig +short example.comand your firewall. - "Could not automatically find a matching server block": the
server_namein Nginx does not include the exact names you passed with-d. - Renewal dry run fails: something changed since the first request, often port 80 being closed or a redirect rule blocking
/.well-known/acme-challenge/. - See your certificates:
sudo certbot certificateslists names and expiry dates.
