What it means

Before issuing a certificate, Let's Encrypt (or any ACME authority) checks that you control the domain: it fetches a file over HTTP (http-01) or reads a TXT record (dns-01). When that check fails, Certbot writes Challenge failed for domain example.com to /var/log/letsencrypt/letsencrypt.log and stops with Some challenges have failed. On screen, current versions print the reason:

Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems:
  Identifier: example.com
  Type:   connection
  Detail: 203.0.113.10: Timeout during connect (likely firewall problem)

Failed to renew certificate example.com with error: Some challenges have failed.

The Type line points to the cause.

Common causes

1. Port 80 is closed (connection). A firewall, a cloud security group or the host blocks port 80. http-01 only works on port 80, even if the site only serves HTTPS. Let's Encrypt fetches the file from several vantage points, so a firewall that only allows some countries fails too. Confirm from outside the server:

curl -I http://example.com/.well-known/acme-challenge/test
sudo ufw status

2. The challenge file is not served (unauthorized, 404 or 403). A rewrite, an app route or a wrong webroot catches /.well-known/acme-challenge/. Confirm with a test file in the webroot and the curl above.

3. DNS points elsewhere (dns or unauthorized). A stale A record, or an AAAA record for an old server: Let's Encrypt prefers IPv6 and only falls back to IPv4 on a timeout, not on a wrong answer.

dig +short A example.com
dig +short AAAA example.com

4. Standalone cannot get port 80. With --standalone, Certbot starts its own server and stops with Could not bind TCP port 80 because it is already in use by another process, the same conflict nginx reports as bind() to 0.0.0.0:80 failed.

5. The TXT record is missing (dns-01). Wildcard certificates need _acme-challenge.example.com. A certificate made with --manual cannot renew alone without an auth hook.

How to fix

  1. Reproduce without burning the rate limit: sudo certbot renew --dry-run.
  2. Read the Type and Detail lines and confirm with the matching command above.
  3. Open port 80 to the whole internet; a redirect to HTTPS afterwards is fine.
  4. Serve the challenge path before any rewrite:
    location ^~ /.well-known/acme-challenge/ { root /var/www/letsencrypt; }
  5. Fix or remove the stale A or AAAA record.
  6. Replace --standalone with --nginx or --webroot, or add --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx".
  7. For wildcards, use a DNS plugin for your provider (for example certbot-dns-cloudflare) instead of --manual.
  8. Check that the renewal job still runs: systemctl list-timers | grep certbot.

Is it just you?

Put a test file in the challenge path and check http://example.com/.well-known/acme-challenge/test from several regions with Is my site down?. A region that times out is what Let's Encrypt sees.

Get alerted next time

  • SSL monitoring reads the certificate your server serves every day and warns you 1 to 90 days before expiry, with reminders. With a 15-day threshold on a 90-day certificate, an alert means renewal has been failing for about two weeks.
  • A healthcheck on the renewal job, certbot renew -q && curl -fsS https://hc.hyperping.io/TOKEN_ID, alerts you the first time a run fails or the job stops running.
  • See SSL monitoring in the docs for thresholds and channels.