toolhq.io

All posts
7 min readby Jameel Haider

DNS-01 vs HTTP-01: choosing an ACME challenge type

Before a certificate authority issues a certificate it has to confirm you control the name you asked for. ACME defines three ways to prove that, and the one you choose determines whether you can issue wildcards, whether the host needs to be reachable from the internet, and what breaks at three in the morning when renewal fails.

Most people never choose. They run the default, it works, and the decision only surfaces later when they need a wildcard or move a service behind a firewall.

HTTP-01

The CA gives your client a token. The client writes it to a file, and the CA fetches it:

http://example.com/.well-known/acme-challenge/<token>

If the content matches, you have proven control.

This is the default in most clients because it needs nothing except a web server you already run. No DNS credentials, no API integration.

The constraints are specific:

  • Port 80 must be reachable from the public internet. The CA always starts on port 80. It will follow a redirect to HTTPS, so a standard HTTP to HTTPS redirect is fine, but a firewall that drops port 80 entirely is not.
  • It cannot issue wildcards. Proving control of one path on one host says nothing about arbitrary subdomains, so the CA will not accept it for *.example.com.
  • Every name in the certificate is validated separately. A four name SAN certificate means four fetches, each of which must succeed.

The most common failure is a redirect or rewrite rule that swallows the challenge path. Anything that sends unknown paths to a single page application, forces authentication globally, or redirects the whole site to a canonical host will intercept the request the CA is making. Exclude /.well-known/acme-challenge/ from that handling explicitly.

The second most common is validating a name whose DNS points somewhere else. The CA resolves the name and fetches from wherever it lands, which may not be the server running your ACME client.

DNS-01

The client publishes a TXT record and the CA looks it up:

_acme-challenge.example.com.  IN TXT  "gfj9Xq...Rg85nM"

The value is derived from the account key and the challenge token, so it cannot be replayed by anyone else.

What this buys you:

  • Wildcards. DNS-01 is the only way to get *.example.com. Control of the zone is what the CA needs to see.
  • The host does not need to be reachable. Nothing connects to the server at all. This is how you get publicly trusted certificates for internal services, machines behind a firewall, load balancer endpoints, and hosts with no inbound access whatsoever.
  • No port requirements. Nothing to open, nothing to exclude from a redirect rule.

The cost is DNS automation. Your ACME client needs credentials for your DNS provider's API, which means storing a token that can modify your zone on whatever machine runs renewals. That is a meaningful amount of authority to leave sitting on a web server.

Two details that cause real trouble:

Propagation timing. The client writes the record, then the CA reads it, possibly from a different resolver. If your provider is slow to propagate, or you run multiple authoritative nameservers that update independently, the CA can read a stale answer and fail the challenge. Most clients wait, but the default wait is sometimes too short. Confirm what your nameservers are actually returning with the DNS propagation checker before assuming the client is at fault.

Multiple TXT records at the same name. Issuing example.com and *.example.com on one certificate produces two separate challenges, both at _acme-challenge.example.com. Both records must exist simultaneously. A DNS plugin that replaces rather than appends will break this, and the symptom is that one name validates and the other does not.

Limiting the blast radius with CNAME delegation

You can get DNS-01 without handing full zone credentials to a web server. Point the challenge name at a zone you are willing to expose:

_acme-challenge.example.com.  IN CNAME  example.com.acme.other-zone.net.

The CA follows the CNAME and reads the TXT from the target. Your ACME client only needs credentials for the delegated zone, which contains nothing but challenge records. If that machine is compromised, the attacker can issue certificates for the delegated names and cannot touch your real DNS.

This is the standard approach for anyone uncomfortable with production DNS credentials on application servers, and it is worth the extra setup on anything internet facing.

TLS-ALPN-01

The third option validates over TLS on port 443. The CA opens a connection requesting the acme-tls/1 protocol via ALPN, and your server answers with a specially constructed self signed certificate containing the challenge response.

It exists for the case where port 80 is unavailable but 443 is open, and for TLS terminating proxies that want to handle validation without involving an HTTP layer at all. It cannot issue wildcards, same as HTTP-01.

Support is narrower. It needs a server that can serve that special certificate on demand, which in practice means a proxy with it built in, such as Caddy or Traefik, or a dedicated standalone mode. It is not the answer for a general Apache or nginx setup.

Choosing

SituationUse
Ordinary public web serverHTTP-01
Wildcard certificateDNS-01, no alternative
Host not reachable from the internetDNS-01
Port 80 blocked, 443 openTLS-ALPN-01, or DNS-01
Many subdomains on one certificateDNS-01, fewer moving parts than n fetches
DNS provider has no APIHTTP-01, or delegate with CNAME
Reluctant to store DNS credentialsHTTP-01, or DNS-01 with CNAME delegation

When issuance fails anyway

Both methods can be blocked by something neither of them touches: a CAA record. If your domain has a CAA record naming a different certificate authority, issuance is refused regardless of how cleanly the challenge validated. This surfaces when an organisation adds CAA records centrally and a team elsewhere is still renewing through another CA. Check what your zone publishes with the DNS lookup tool and read CAA records explained for the syntax.

After issuance, confirm what the server is presenting rather than trusting that the client wrote the files correctly. The SSL checker shows the served chain, expiry and names, which catches the common case where a certificate renewed successfully but the service was never reloaded to pick it up.

Related reading: how Let's Encrypt issues a certificate walks through the full ACME order flow, and monitor SSL certificate expiry covers catching a silently failing renewal before it expires.