What the validator does with your DNS
/.well-known/acme-challenge/ on your web server. Let's Encrypt then has to fetch that file from the outside to prove you control the domain.
The validation service does not use your browser's logic or your laptop's resolver. It runs its own DNS lookups and asks for both record types. When it gets back an A record and a AAAA record, Let's Encrypt typically prefers the IPv6 address and opens the connection there first. The same applies to the TLS-ALPN-01 challenge, which connects on port 443 instead of port 80. DNS-01 is different: it only reads a TXT record, so a stale AAAA has no effect on it.
So the sequence looks like this:
- The validator resolves
example.comand gets both an IPv4 and an IPv6 address. - It picks the IPv6 address and sends a TCP SYN (the first packet of a connection) to port 80 on that address.
- Whatever machine owns that address decides what happens next.
- If the connection succeeds, the validator sends
GET /.well-known/acme-challenge/<token>with your domain in the Host header and checks the response body against the expected token.
Three ways a stale IPv6 address fails
example.com is fine but it redirects to www.example.com, and www has the stale AAAA, the failure happens on the second hop.
Why your own tests say everything is fine
curl from the new server itself usually goes over IPv4 or loopback, which proves nothing about the AAAA record.
The most useful clue is in the error message. Most ACME clients print the IP address the validator connected to. If that address contains colons, the validator used IPv6, and you should compare it with what your server actually has. The exact wording depends on the client or the control panel, but the address is usually there.
Finding the stale record with dig
BASH
# Which nameservers are authoritative for the zone
dig +short NS example.com
# Ask one of them directly for both record types
dig +short A example.com @ns1.example-dns.net
dig +short AAAA example.com @ns1.example-dns.net
# Repeat for every name on the certificate
dig +short AAAA www.example.com @ns1.example-dns.netns1.example-dns.net with one of the nameservers returned by the first command. If you get an IPv6 address back, compare it with the addresses actually configured on the server that should be answering:
BASH
# IPv6 addresses configured on this server (global scope only)
ip -6 addr show scope global
# Test the challenge path over IPv6 only, from a machine with working IPv6
curl -6 -sI 'http://example.com/.well-known/acme-challenge/test'ip output, the record points somewhere else. The curl -6 test needs to run from a machine that has working IPv6 connectivity, otherwise it fails for reasons unrelated to your DNS. A 404 on a made-up filename is expected, so focus on which server responds. Response headers such as Server or a different set of cookies often show that you reached a different machine than you expected.
Fixing it and keeping it fixed
listen [::]:80; line in the server block. If the new server has no IPv6, or you are not ready to support it, delete the AAAA record. A missing AAAA is harmless. A wrong one is not.
After the change, give caches time to expire. Resolvers, including the ones the CA uses, may keep the old answer until the record's TTL runs out, so retrying a renewal one minute after editing the zone can fail for reasons that are already fixed. Check the authoritative answer again with dig, wait out the TTL, then retry.
The longer-term fix is making AAAA records part of routine DNS maintenance rather than something you only look at when a renewal breaks. A few habits help. When a server is migrated or retired, review every AAAA record that pointed at it, not just the A records. When importing a zone from an old provider or a backup, treat imported AAAA records as suspect until you confirm the address belongs to a live host you control. On a regular cycle, compare each published AAAA against the addresses on the server it should point to. This is a short check, and it fits naturally next to the other DNS checks that affect issuance, such as CAA records, which are covered in Fixing CAA Failures When Switching from Manual SSL to ACME Clients.
When a renewal fails and the A record looks right, read the IP address in the error before anything else. If it is an IPv6 address, run dig AAAA against the authoritative nameservers for every name on the certificate and compare the result with ip -6 addr on the real server. Most of the time the fix is either correcting one record or deleting it, and the harder part is remembering that the domain had a second address in the first place.