HTTP-01 vs DNS-01: Choosing an ACME Challenge
Compare HTTP-01 and DNS-01 ACME challenges, pick the one that fits nginx on a VPS versus wildcards, and avoid the common failure modes of each.
Netbay Developer Relations
Netbay Engineering
On this page
ACME does not trust you because you have SSH. It trusts you because you prove control of the name with a challenge. Two challenges cover almost every VPS: HTTP-01, which publishes a token on port 80, and DNS-01, which publishes a TXT at _acme-challenge. Choosing wrong is how you spend an evening fighting a firewall, or how you cannot issue a wildcard at all. This post is the decision, the exact records and paths, and the ways each challenge fails.
TLS-ALPN-01 exists and is useful when you control a dedicated TLS listener on 443, but certbot plus nginx on a typical Ubuntu VPS still starts with HTTP-01. Wildcards require DNS-01. That one sentence already decides most setups.
HTTP-01: the CA fetches a file from you
Let's Encrypt gives you a token. Certbot writes it under /.well-known/acme-challenge/ on port 80. The CA's validators resolve the A/AAAA of the name and GET the URL. If the body matches, you proved you control the HTTP service for that name. That is why the A record must already point at this VPS, why port 80 must be reachable from the world, and why a reverse-proxy default that redirects everything to HTTPS before the location block can break the GET.
Common HTTP-01 failures:
- A or AAAA points at an old host or a broken IPv6 path.
- Host firewall, cloud security group, or nginx not listening on 80.
- A global HTTPS redirect that does not exclude the challenge path.
- The token served for the wrong server_name.
# prove HTTP-01 plumbing before certbot
mkdir -p /var/www/example/.well-known/acme-challenge
echo "token-test" > /var/www/example/.well-known/acme-challenge/ping
curl -4 -sS http://example.com/.well-known/acme-challenge/ping
certbot certonly --webroot -w /var/www/example -d example.com -d www.example.com --dry-runHTTP-01 cannot issue *.example.com. The challenge is bound to a specific hostname the CA can request over HTTP. If you need a wildcard, stop trying to clever-redirect HTTP-01 and switch to DNS-01.
DNS-01: the CA fetches a TXT from the zone
DNS-01 asks you to publish a TXT at _acme-challenge.example.com (and _acme-challenge.www.example.com if you also want www as a SAN). The CA queries DNS, not your web server. Port 80 can be closed. The name can even point at a host that is not yet live. That is why DNS-01 is used for wildcards, for hosts behind a closed firewall, and for names that do not speak HTTP.
The cost is operational. Someone must write that TXT and delete it later. Doing it by hand in a registrar UI works once. Renewal every 60 days does not. You want a DNS plugin (Cloudflare, Route 53, PowerDNS, your registrar API) so certbot can create and remove the record. API tokens should be scoped to TXT on _acme-challenge if the provider allows.
# after the plugin writes the TXT, check the public view
dig +short TXT _acme-challenge.example.com @1.1.1.1
dig +short TXT _acme-challenge.example.com @8.8.8.8
certbot certonly --dns-cloudflare --dns-cloudflare-credentials /root/.secrets/cf.ini -d example.com -d "*.example.com"CNAME-ing _acme-challenge to a dedicated ACME zone is a valid pattern if you do not want the registrar API on the VPS. The CA follows the CNAME and reads the TXT at the target. Do not CNAME _acme-challenge to the apex; give it its own name.
Which one on a single VPS
Use HTTP-01 when:
- nginx already listens on 80 for the site names.
- You need example.com and www.example.com, not a wildcard.
- You do not want a DNS API token on the box.
Use DNS-01 when:
- You need *.example.com.
- Port 80 is not available (captive portal, filtered, or HTTP disabled on purpose).
- The HTTP vhost is a maze of redirects you do not want certbot to touch.
Do not mix challenges in a way renewal cannot repeat. If issuance used HTTP-01, renewal expects port 80 still open. If issuance used DNS-01, renewal expects the API credentials still valid. The renewal hook is part of the challenge choice.
Rate limits apply to both. Failed challenges still count toward some limits. Dry-run uses the staging environment and is the right way to test a new challenge path. When you change from HTTP-01 to DNS-01, do it on staging first, then production, then confirm certbot renew --dry-run.
HTTP-01 is the default for nginx on a public VPS. DNS-01 is the tool for wildcards and closed port 80. Pick one, automate it, and verify with curl or dig from a public resolver before you blame certbot. You can test both paths on an Ubuntu VPS at Netbay in Lucknow (DC01), with High-Speed SSD and L3/L4 DDoS filtering already on the public IP — netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance