Let's Encrypt Certificate Renewal Failed: Fix Guide
Let's Encrypt certificates are valid for 90 days, and Certbot is designed to renew them automatically 30 days before expiry — around the 60-day mark. When renewal fails silently, the first sign is usually a browser warning that your site's certificate has expired, sometimes days after the actual failure. By then visitors have already seen the scary "Not Secure" warning, so catching renewal problems early is essential.
Renewal uses the ACME protocol, which proves domain control by either serving a challenge file over HTTP on port 80 (webroot or standalone mode) or via DNS. The most common renewal failures stem from four causes: port 80 is occupied by a running web server that is not configured to serve the challenge, the DNS A record no longer points to the server, the webroot path changed since the original issuance, or a rate limit was hit after repeated failed attempts. The five steps below cover diagnosis through to a self-healing renewal setup.
Step 1: Check the Renewal Error Logs
Start by reproducing the failure with a dry run, which simulates renewal without hitting Let's Encrypt's rate limits:
sudo certbot renew --dry-run
The output names the failing certificate and the specific error. For fuller detail, read the log directory:
sudo ls -lt /var/log/letsencrypt/
sudo tail -80 /var/log/letsencrypt/letsencrypt.log
Common errors you will see: "Connection refused" (port 80 unreachable), "Unauthorized" (challenge file not served), "DNS problem: NXDOMAIN" (DNS misconfigured), or "too many failed authorizations" (rate limited). The error type determines which of the next steps applies.
Step 2: Fix Port 80 Conflicts
The HTTP-01 challenge requires Let's Encrypt to fetch a file from http://your-domain/.well-known/acme-challenge/. If Nginx or Apache is already running, standalone mode cannot bind to port 80 and renewal fails with "Problem binding to port 80: Could not bind to IPv4 or IPv6."
The cleanest fix is to use webroot mode, which lets your running web server serve the challenge file. Check how the certificate was originally issued:
sudo certbot certificates
If it used standalone, switch to webroot by editing the renewal configuration file at /etc/letsencrypt/renewal/example.com.conf:
[renewalparams]
authenticator = webroot
webroot_path = /var/www/html,
[[webroot_map]]
example.com = /var/www/html
Ensure Nginx serves the challenge path from that webroot:
location ^~ /.well-known/acme-challenge/ {
root /var/www/html;
default_type "text/plain";
}
Step 3: Verify DNS Is Correct
The ACME challenge resolves your domain to an IP and connects to that IP on port 80. If the A record points elsewhere — for example, after a server migration or because Cloudflare's proxy is intercepting the request — renewal fails with an authorization error.
dig +short example.com
Confirm the IP matches your server. If you use Cloudflare, temporarily disable the proxy (set the record to DNS-only / grey cloud) during renewal, or use Cloudflare's own origin certificate instead. DNS propagation can also be an issue: if you changed records recently, wait for the TTL to expire before retrying.
Step 4: Fix Webroot or Standalone Mode Issues
If the error is "Client with the currently selected authenticator does not support any combination of challenges," the TLS-SNI challenge was deprecated and your old config needs updating. Reissue using webroot with an explicit path:
sudo certbot certonly --webroot -w /var/www/html \
-d example.com -d www.example.com --force-renewal
For standalone mode, stop the web server first so Certbot can bind port 80:
sudo systemctl stop nginx
sudo certbot renew
sudo systemctl start nginx
This works but is disruptive, so prefer webroot mode for production. Also confirm the webroot path still exists and is writable — a common failure after restructuring a site is that /var/www/html moved but the renewal config still references it.
Step 5: Set Up Auto-Renewal and Monitoring
Certbot installs a systemd timer (or cron job) that runs renewal twice daily. Verify it is enabled:
# systemd timer
sudo systemctl list-timers | grep certbot
sudo systemctl enable --now certbot.timer
The timer runs certbot -q renew, which only acts on certificates near expiry and reloads the web server afterward via a deploy hook. Add a post-renewal hook to reload Nginx:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh <<'EOF'
#!/bin/bash
systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Finally, add monitoring so a failed renewal alerts you before expiry. The simplest approach is a check script that reports when any certificate is within 7 days of expiry, which you can wire into your existing monitoring:
sudo certbot certificates | grep "Expiry Date"
Better yet, combine a deploy hook with Uptime Robot or a health check that validates your HTTPS endpoint daily. A certificate that fails to renew should page you, not your visitors.
A common monitoring pitfall is checking only that port 443 is open, which stays true even after the certificate expires. A meaningful health check must actually validate the certificate chain and its expiry date. Tools like certbot renew --dry-run in a weekly cron job, combined with an external probe that sends an alert when the certificate has fewer than 14 days remaining, give you a comfortable buffer to fix problems before any browser shows a warning.
Quick Reference: Causes and Fixes
| Symptom / Log Message | Root Cause | Fix |
|---|---|---|
| Problem binding to port 80 | Web server occupies port 80 | Use webroot mode; configure .well-known |
| Unauthorized (challenge not served) | Wrong webroot path | Update webroot_path in renewal config |
| DNS problem: NXDOMAIN | A record missing or wrong | Fix DNS A record; disable Cloudflare proxy |
| too many failed authorizations | Rate limit from repeated failures | Wait 1 hour; use --dry-run to test |
| Challenge supports no combination | Deprecated TLS-SNI authenticator | Reissue with --webroot |
| Certificate expired with no alert | Timer disabled / no monitoring | Enable certbot.timer; add expiry monitoring |