You check your phone and there’s a message from a user: “Your website says it’s not secure. I can’t access it.”
You open the site in your browser and see the dreaded red warning: “Your connection is not private.” You check the certificate — expired three days ago. The Let’s Encrypt auto-renewal that’s supposed to handle this silently in the background? It failed. And you didn’t notice until visitors started complaining.
This happens to more websites than anyone wants to admit. Let’s Encrypt certificates only last 90 days, and if the auto-renewal breaks for any reason, you have a ticking clock until your site goes dark. The good news: fixing it takes about two minutes. The better news: I’ll show you how to make sure it never happens again.
Step 1: Check the Damage
SSH into your server and see what Certbot knows:
sudo certbot certificates
This shows all certificates Certbot manages on your server. You’ll see something like:
Certificate Name: yourdomain.com
Domains: yourdomain.com www.yourdomain.com
Expiry Date: 2026-02-15 (EXPIRED)
Certificate Path: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
Key Path: /etc/letsencrypt/live/yourdomain.com/privkey.pem
There it is — EXPIRED. Now let’s fix it.
If certbot isn’t installed (maybe someone else set up the server):
# Debian/Ubuntu
sudo apt update && sudo apt install certbot
# CentOS/RHEL
sudo dnf install certbot
# With Nginx plugin
sudo apt install python3-certbot-nginx
# With Apache plugin
sudo apt install python3-certbot-apache
Step 2: Renew the Certificate
The simplest command:
sudo certbot renew
Certbot checks all certificates and renews any that are expired or close to expiring. If everything goes smoothly, you’ll see:
Congratulations, all renewals succeeded.
But if it were that easy, you probably wouldn’t be reading this article. Let’s handle the failures.
When Renewal Fails: The Common Errors
“Problem binding to port 80”
Could not bind to IPv4 or IPv6.. Skipping.
Something else is using port 80. Usually it’s your web server (Nginx or Apache) and Certbot needs port 80 for the HTTP challenge.
Fix for Nginx:
sudo certbot renew --nginx
This tells Certbot to use the Nginx plugin, which handles the challenge through the running Nginx process instead of needing to bind to port 80 directly.
Fix for Apache:
sudo certbot renew --apache
Fix if neither works — temporarily stop the web server:
sudo systemctl stop nginx # or apache2
sudo certbot renew
sudo systemctl start nginx # or apache2
Your site will be down for a few seconds during renewal. Not ideal, but it works.
“Connection refused” or “DNS problem”
Failed to connect to host for DVSNI challenge
DNS problem: NXDOMAIN looking up A for yourdomain.com
The Let’s Encrypt servers can’t reach your server. Possible causes:
Firewall blocking port 80. Let’s Encrypt needs to connect to port 80 on your server to verify domain ownership. Check your firewall:
sudo ufw status # Ubuntu
sudo firewall-cmd --list-ports # CentOS
If port 80 isn’t open:
sudo ufw allow 80/tcp # Ubuntu
sudo firewall-cmd --permanent --add-port=80/tcp && sudo firewall-cmd --reload # CentOS
Also check your cloud provider’s firewall (AWS Security Groups, DigitalOcean Firewall, etc.) — port 80 must be open there too.
DNS doesn’t point to this server. If you recently moved servers or changed DNS, the domain might point to the old IP. Check:
dig yourdomain.com +short
The IP returned should match your server’s IP. If it doesn’t, update your DNS records.
“.htaccess is blocking the challenge”
This affects Apache servers with restrictive .htaccess rules. The ACME challenge creates a file at /.well-known/acme-challenge/ and Let’s Encrypt needs to read it over HTTP. If .htaccess redirects everything to HTTPS or blocks access to dotfiles, the challenge fails.
Temporary fix:
sudo mv /var/www/html/.htaccess /var/www/html/.htaccess_backup
sudo certbot renew
sudo mv /var/www/html/.htaccess_backup /var/www/html/.htaccess
Permanent fix — add this to your .htaccess before any redirect rules:
RewriteEngine On
RewriteRule ^\.well-known/acme-challenge/ - [L]
This lets ACME challenges through while keeping all other rules intact.
“Too many failed authorizations recently”
Let’s Encrypt has rate limits. If you’ve been trying to renew and failing repeatedly, you might hit the limit. The error tells you when you can try again — usually after an hour. Wait it out, fix the underlying problem, then try again.
Step 3: Force Renewal When Normal Renewal Doesn’t Work
If certbot renew doesn’t pick up your certificate (maybe because it thinks it’s not due yet), force it:
sudo certbot renew --cert-name yourdomain.com --force-renewal
Or if everything is completely broken, start fresh:
# For Nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# For Apache
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
This obtains a completely new certificate and automatically configures your web server to use it.
Step 4: Restart Your Web Server
After renewal, the new certificate files exist on disk but your web server might still be serving the old, expired certificate from memory. Restart it:
sudo systemctl restart nginx
# or
sudo systemctl restart apache2
Verify the new certificate is live:
sudo openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates
You should see the new expiry date — 90 days from now.
Or just open your website in a browser. The security warning should be gone, replaced by the familiar padlock icon.
Step 5: Fix Auto-Renewal So This Never Happens Again
Certbot installs either a systemd timer or a cron job to handle auto-renewal. Let’s make sure it’s working.
Check the systemd timer:
sudo systemctl status certbot.timer
If it shows active (waiting), auto-renewal is scheduled. If it shows inactive or not found:
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
Check cron jobs:
sudo cat /etc/cron.d/certbot
Or check the root crontab:
sudo crontab -l
You should see a line that runs certbot renew periodically, something like:
0 */12 * * * root certbot renew --quiet
If there’s no cron job and no timer, add one:
sudo crontab -e
Add this line:
0 3 * * * certbot renew --quiet --deploy-hook "systemctl restart nginx"
This runs renewal at 3 AM every day. The --deploy-hook automatically restarts Nginx only when a certificate is actually renewed — not every day.
Replace nginx with apache2 if you use Apache.
Test that auto-renewal works:
sudo certbot renew --dry-run
A dry run simulates the renewal process without actually changing anything. If it succeeds, you’re good. If it fails, the error message tells you what to fix.
Monitor Your Certificates
Auto-renewal usually works. But “usually” isn’t good enough when your website’s reputation is at stake. Add a monitoring layer:
Let’s Encrypt sends expiry emails. Make sure the email in your Certbot config is correct:
sudo cat /etc/letsencrypt/renewal/yourdomain.com.conf | grep email
If the email is wrong or missing, update it:
sudo certbot update_account --email your@email.com
Let’s Encrypt sends warning emails at 20 days, 10 days, and 1 day before expiry. If you get one of these emails, auto-renewal has failed and you need to act.
Use an external monitor. Services like UptimeRobot, Better Uptime, or Pingdom can check your HTTPS endpoint and alert you when the certificate is about to expire. The free tier of UptimeRobot supports SSL monitoring with email and webhook alerts.
Check manually once a month. Run sudo certbot certificates and verify all certificates have expiry dates in the future. Takes ten seconds and gives you peace of mind.
Wildcard Certificates (Special Case)
If you have a wildcard certificate (*.yourdomain.com), renewal is different because wildcard certificates require DNS-01 challenges instead of HTTP-01. This means Certbot needs to create a DNS TXT record to prove domain ownership.
If you used the DNS plugin during initial setup:
sudo certbot renew
Should work automatically. If it doesn’t, you may need to provide DNS API credentials again:
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d yourdomain.com -d *.yourdomain.com
Replace cloudflare with your DNS provider’s plugin (route53, digitalocean, etc.).
Prevention Checklist
After fixing the expired certificate, run through this checklist to prevent it from happening again:
Certbot timer or cron is active — check with systemctl status certbot.timer or crontab -l
Dry run succeeds — sudo certbot renew --dry-run completes without errors
Port 80 is open — firewall and cloud security groups allow HTTP traffic
Email is correct — Let’s Encrypt expiry warnings go to an email you actually read
Web server restarts after renewal — deploy hook is configured in the renewal config or cron job
External monitoring is set up — an outside service checks your HTTPS and alerts on certificate issues
Your certificate is renewed, your website is secure again, and auto-renewal is set up properly. The whole thing should have taken less than five minutes. And with the right monitoring in place, you’ll never have an expired certificate surprise again.