Terminal output showing Let's Encrypt certbot renewal error with ACME challenge failure and permission denied messages
# developer tools# website monitoring

Let's Encrypt Renewal Failed: Common Causes and Fixes

Let's Encrypt certificates expire every 90 days. The short lifespan is intentional — it encourages automation and limits the damage from compromised certificates. But it also means that when the automated renewal process breaks, you have less time to notice and fix it than you would with a 1-year or 2-year certificate.

The most common scenario: renewal worked fine for months, something changed (new server config, DNS change, firewall rule), renewal failed silently, and you found out when users started seeing browser warnings.

Here's how to diagnose and fix the most common renewal failures.


Check What Happened First

Before troubleshooting, check the renewal logs:

# Check the Certbot renewal log
cat /var/log/letsencrypt/letsencrypt.log | tail -100

# Check systemd logs for the Certbot timer
journalctl -u certbot.timer -n 50
journalctl -u certbot.service -n 50

# See when renewal last ran and whether it succeeded
certbot certificates

The certbot certificates command shows your certificates and their expiry dates. If a certificate is expiring within 30 days and you expected auto-renewal to have handled it, renewal failed.


Cause 1: HTTP-01 Challenge Failing (Most Common)

Let's Encrypt validates domain ownership by placing a file at http://yourdomain.com/.well-known/acme-challenge/TOKEN and checking it's accessible. If that URL can't be reached, validation fails.

Error message:

Timeout during connect (likely firewall problem)
Connection refused
404 Not Found for /.well-known/acme-challenge/

Check if the challenge path is accessible:

# Test that the ACME challenge path is reachable
curl -I http://yourdomain.com/.well-known/acme-challenge/test

# Create a test file to verify the path serves content
mkdir -p /var/www/yourdomain/.well-known/acme-challenge/
echo "test" > /var/www/yourdomain/.well-known/acme-challenge/testfile
curl http://yourdomain.com/.well-known/acme-challenge/testfile

Common reasons HTTP-01 fails:

HTTPS redirect blocking HTTP-01 checks: If your Nginx/Apache redirects all HTTP traffic to HTTPS, the challenge file at the HTTP URL isn't reachable.

# Nginx — add exception before the redirect
server {
    listen 80;
    server_name yourdomain.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
        # Allow before redirect
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

Firewall blocking port 80:

# Check if port 80 is accessible from outside
curl -I http://yourdomain.com

# Check firewall rules
ufw status
# or
iptables -L INPUT -n | grep -E "80|http"

Let's Encrypt validation comes from their servers, so firewalls that block certain IPs or all external traffic to port 80 will cause failures.

Webroot path mismatch:

# Check your renewal configuration
cat /etc/letsencrypt/renewal/yourdomain.com.conf
# Look for: webroot_path = /var/www/...
# Ensure this path exists and nginx/apache serves files from it

Cause 2: DNS-01 Challenge Failing

If you're using DNS-01 validation (required for wildcard certs), the challenge requires creating a TXT record at _acme-challenge.yourdomain.com.

Error message:

DNS problem: NXDOMAIN looking up TXT for _acme-challenge.yourdomain.com

Check if your DNS plugin is configured correctly:

# For Cloudflare plugin
cat /etc/letsencrypt/renewal/yourdomain.com.conf
# Look for: dns_cloudflare_credentials = /etc/letsencrypt/cloudflare.ini

Common DNS-01 failures:

  • API credentials expired (Cloudflare API token revoked)
  • DNS propagation didn't complete before validation (add --dns-cloudflare-propagation-seconds 60)
  • Wrong API token permissions (token needs Zone → DNS → Edit permissions)

Cause 3: Port 443 Open But Certificate Already Expired

If your certificate has already expired, some Certbot configurations fail to renew because the TLS connection itself fails during the process.

# Force renewal ignoring the TLS check
certbot renew --force-renewal --no-self-upgrade

# If using standalone mode (Certbot handles its own HTTP server)
# Stop nginx first, run Certbot, restart nginx
systemctl stop nginx
certbot renew
systemctl start nginx

Cause 4: Rate Limits Hit

Let's Encrypt has rate limits: 50 certificates per registered domain per week. If you've been repeatedly issuing certificates (during testing or failed attempts), you may hit the limit.

Error message:

too many certificates already issued for exact set of domains
Error: rateLimited

Check if you're rate limited:

# Let's Encrypt publishes rate limit status
# Check: https://crt.sh/?dNSName=yourdomain.com&exclude=expired
# This shows recent certificates issued for your domain

If rate limited, you must wait. Use the staging environment for testing:

# Use staging environment to test without affecting rate limits
certbot renew --dry-run --staging

Cause 5: Certbot Timer Not Running

The renewal process is triggered by a systemd timer (or cron job on older systems). If the timer stopped or was accidentally disabled:

# Check timer status
systemctl status certbot.timer

# Enable and start if stopped
systemctl enable certbot.timer
systemctl start certbot.timer

# Check cron (for older installations)
crontab -l | grep certbot
ls /etc/cron.d/ | grep certbot

Cause 6: Domain No Longer Points to This Server

If you migrated to a new server but the old server's Certbot is still trying to renew, or you changed DNS before decommissioning the old server, validation will fail because HTTP requests hit the wrong server.

# Verify your domain resolves to this server's IP
dig yourdomain.com A +short
curl ifconfig.me  # This server's public IP

If the IPs don't match, either:

  • Fix the DNS to point to this server
  • Transfer the certificate renewal to the new server
  • Switch to DNS-01 validation which doesn't require the server to be reachable

Fixing and Testing

Once you've identified the cause, force a renewal to test:

# Test renewal without making changes (safe to run any time)
certbot renew --dry-run

# Force renewal even if certificate isn't near expiry
certbot renew --force-renewal

# Renew a specific certificate
certbot renew --cert-name yourdomain.com --force-renewal

After successful renewal, verify nginx/Apache reloaded:

# Check the certificate being served
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -dates

The notAfter date should reflect the newly renewed certificate (90 days from now).


Setting Up Renewal Monitoring

The only reliable way to catch renewal failures before users are affected is external certificate monitoring.

Domain Monitor monitors your SSL certificates and alerts you when expiry is approaching — regardless of why renewal failed. Whether it's Certbot, a DNS hook broken by an API change, or a misconfigured reload script, you'll receive an alert with enough time to fix it before any user sees a browser warning. Create a free account and add SSL monitoring alongside your uptime checks.

See how to rotate SSL certificates for manual certificate rotation steps, and complete guide to SSL certificates for a full overview of SSL certificate management.


Also in This Series

More posts

Why Your Status Page Matters During an Outage

When your site goes down, your status page becomes the most important page you have. Here's why it matters, what happens when you don't have one, and what a good status page does during a real outage.

Read more
Why Your Domain Points to the Wrong Server

Your domain is resolving, but pointing to the wrong server — showing old content, a previous host's page, or someone else's site entirely. Here's what causes this and how to diagnose it.

Read more
Why Website Monitoring Misses Downtime Sometimes

Uptime monitoring isn't foolproof. Single-location monitors, wrong health check endpoints, long check intervals, and false positives can all cause real downtime to go undetected. Here's what to watch out for.

Read more

Subscribe to our PRO plan.

Looking to monitor your website and domains? Join our platform and start today.