certbot with -d '*.example.com', the command you have always used for single domains, and it refuses. The cause is not your web server or your firewall. Let's Encrypt only issues wildcard certificates through the DNS-01 challenge, and the usual Certbot setup uses HTTP-01. Which challenge you choose also decides how renewal works later, so make that choice before you request anything.
This guide covers when each challenge fits and what automated DNS-01 needs from your DNS provider. It then walks through the commands that confirm renewal will pass while the current certificate still has plenty of time left.
Why HTTP-01 cannot issue a wildcard
http://example.com/.well-known/acme-challenge/<token>. The CA then fetches it over port 80. A successful fetch proves you control the web server that the name points to.
That proof only works for one specific hostname. *.example.com stands for every possible subdomain: shop, staging, anything-you-make-up-tomorrow. No single HTTP request can prove you control all of them. So the CA's policy is that wildcards require DNS-01.
With DNS-01, you publish a TXT record at _acme-challenge.example.com. Control of the DNS zone is a stronger claim than control of one web server, which is why the CA accepts it for a wildcard.
In Certbot terms, the --webroot, --nginx, --apache, and --standalone authenticators all use HTTP-01. Ask any of them for a wildcard and Certbot reports that the selected authenticator cannot satisfy the challenges the CA offers, and suggests a DNS-capable plugin. The exact message varies by version.
Two details trip people up next:
- A wildcard covers exactly one label.
*.example.comcoverswww.example.combut notexample.comitself and nota.b.example.com. Most people request bothexample.comand*.example.comon the same certificate. - When you request both names, both are validated with TXT records at the same name,
_acme-challenge.example.com. Two different values must exist there at the same time. DNS plugins handle this. Copying the values by hand often goes wrong here, because people replace the first value instead of adding a second one.
When HTTP-01 is still the better choice
example.com, www, mail, and shop, and they all point at a server that answers on port 80, a single certificate listing those names works fine with HTTP-01. It needs no DNS credentials on the server. If one subdomain is hosted somewhere else, that is often a sign it should have its own certificate anyway.
DNS-01 earns its place in a few cases:
- Subdomains are created on demand, such as per-customer or per-branch sites, multisite networks, or preview environments.
- The server is not reachable on port 80 from the internet, such as internal hosts or servers behind a firewall that only opens 443.
- You want one certificate on a load balancer or proxy for many backends.
What automated DNS-01 needs from your DNS provider
certbot --manual --preferred-challenges dns and paste TXT values into a control panel yourself. That gets you a certificate today. Renewal is where it breaks.
certbot renew runs unattended from a timer or cron job, and the manual plugin has no way to ask you to edit DNS at that point. Unless you supplied --manual-auth-hook and --manual-cleanup-hook scripts that do the DNS edit, renewal fails. You usually find out when the certificate expires.
For renewal to run on its own, you need three things:
- DNS hosted somewhere with an API. Check where the zone is actually served from (see the
dig NScheck below). It is not always your registrar. - An API credential that can create and delete TXT records in that zone, ideally restricted to that zone and nothing else. The name of the minimum permission differs per provider, so check their documentation.
- A Certbot DNS plugin for that provider, or a hook script that calls the API. Official and third-party plugins exist for many providers. Install the plugin the same way Certbot itself was installed (snap, distro package, or pip). A version mismatch is a common reason Certbot does not list the plugin.
_acme-challenge.example.com with a CNAME to a record in a zone that does have an API. The CA follows that CNAME during validation. The idea is simple, but it needs care to set up, and confirming that it works in your environment is part of the job.
Check DNS before you request
example.com with your domain and ns1.provider.example with one of the nameservers the first command returns.
BASH
# 1. Which nameservers actually serve the zone? Your API credential must be for THIS provider.
dig NS example.com +short
# 2. Is there a CAA record? If so, letsencrypt.org must be allowed.
# Wildcards are governed by 'issuewild' when present, otherwise by 'issue'.
dig CAA example.com +short
# 3. During a request or dry run, watch the TXT record appear.
# Ask the authoritative server directly to bypass resolver caches.
dig TXT _acme-challenge.example.com @ns1.provider.example +short
# 4. Any leftover CNAME on _acme-challenge? Fine if intentional, a problem if forgotten.
dig CNAME _acme-challenge.example.com +shortissue but block wildcards through an issuewild tag. In that case example.com validates and *.example.com gets refused, which looks random until you read the CAA output.
Stale TXT records from old manual attempts usually do no harm, since the CA accepts any matching value. They do make step 3 confusing to read, so clean them up once you have automated renewal.
Request with a DNS plugin and confirm what renewal will use
chmod 600). Certbot warns if the permissions are looser.
BASH
# Plugin and flag names are provider-specific; check your plugin's docs
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini \
-d 'example.com' -d '*.example.com'
# See what Certbot saved for future renewals
sudo grep -E 'authenticator|credentials|hook|server' /etc/letsencrypt/renewal/example.com.confauthenticatornames your DNS plugin, or it saysmanualand amanual_auth_hookline exists. Amanualauthenticator with no hook means renewal will not complete on its own.- The credentials path exists and is still valid. Rotating an API token without updating this file is an easy way to break renewal months later.
- If you migrated from an HTTP-01 setup, check that the old authenticator line is gone and was not just left behind next to the new one.
Prove renewal passes before it matters
BASH
# Full renewal against the Let's Encrypt staging server; does not replace the live cert
sudo certbot renew --dry-run --cert-name example.com
# Is something scheduled to run renewals? Unit names vary by install method.
systemctl list-timers | grep -i certbot
# What Certbot has on disk, including expiry and the names covered
sudo certbot certificates
# What the server actually serves, for the bare name and a random subdomain
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -enddate -ext subjectAltName
echo | openssl s_client -connect example.com:443 -servername test123.example.com 2>/dev/null \
| openssl x509 -noout -enddate -ext subjectAltName--dry-run goes through the full process: the plugin creates the TXT records, the staging CA validates them, and the plugin cleans them up. It does not touch your real certificate.
If it fails, the error usually points to one of three causes:
- Authentication with the DNS API failed, which means a bad or expired credential.
- The CA could not find the TXT record. This is usually a propagation delay or the wrong zone. Rerun the
dig TXTcheck while a dry run is in progress. - CAA refused the request.
certbot renew.
The last two openssl lines catch a different problem: renewal succeeds, but the web server keeps serving the old certificate because it was never reloaded. Compare the notAfter date with what certbot certificates reports. A mismatch after a real renewal means you need a deploy hook that reloads the web server. The random-subdomain test confirms that the wildcard is the certificate actually being presented, as long as that hostname resolves to the server.
Decide the challenge type based on what you need to cover, not on habit. A fixed list of reachable hostnames works with HTTP-01 and needs no DNS credentials. Wildcards and hosts that are not publicly reachable need DNS-01, and that is only worth doing if a plugin or hook can edit DNS without you. Once it is set up, check the renewal file, run certbot renew --dry-run, and compare the served certificate's expiry with the one on disk. Doing that weeks before expiry is far easier than fixing it after browsers start showing warnings.