I’m building a DNS-01 validation client in Python so I can request wildcard certificates, but I keep getting this error from the Let’s Encrypt staging endpoint for any domain I try:
During secondary validation: DNS problem: SERVFAIL looking up TXT for _acme-challenge.dabigg.com - the domain's nameservers may be malfunctioning
The flow I’m implementing is:
- An NS record is created in the Azure DNS zone for _acme-challenge..com which points to my resolver, which is hosted on UDP/TCP 53.
- Submit a new order (for a wildcard).
- On successful response, store the order/challenge data in a local database.
- My own DNS resolver answers the TXT record under
_acme-challenge.<domain>from that database.
Here’s an example challenge response from the ACME server:
{
"identifier": {
"type": "dns",
"value": "dabigg.com"
},
"status": "invalid",
"expires": "2025-12-03T02:11:03Z",
"challenges": [
{
"type": "dns-01",
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall/245712243/20417969703/dUxBhA",
"status": "invalid",
"validated": "2025-11-26T02:11:04Z",
"error": {
"type": "urn:ietf:params:acme:error:dns",
"detail": "During secondary validation: DNS problem: SERVFAIL looking up TXT for _acme-challenge.dabigg.com - the domain's nameservers may be malfunctioning",
"status": 400
},
"token": "v5Zgs2UMkoHDrArUdMFZan9_QE2_8g_lFGNd2kJfVhQ",
"validationRecord": [
{
"hostname": "dabigg.com",
"addressUsed": ""
}
]
}
],
"wildcard": true
}
The part that’s confusing me is that from my side (and several public resolvers), the TXT record looks fine.
The zone dabigg.com is hosted on Azure DNS, but I’ve delegated _acme-challenge.dabigg.com to my own authoritative resolver:
dabigg.com. 172800 IN NS ns1-35.azure-dns.com.
dabigg.com. 172800 IN NS ns2-35.azure-dns.net.
dabigg.com. 172800 IN NS ns3-35.azure-dns.org.
dabigg.com. 172800 IN NS ns4-35.azure-dns.info.
_acme-challenge.dabigg.com. 300 IN NS jh1.pgh.resolution.certaas.web-infra.io.
My resolver at jh1.pgh.resolution.certaas.web-infra.io serves the TXT record based on what’s in the local database.
Here are some tests I ran after placing the TXT:
dig TXT _acme-challenge.dabigg.com @8.8.8.8
dig TXT _acme-challenge.dabigg.com @1.1.1.1
dig TXT _acme-challenge.dabigg.com @9.9.9.9
dig TXT _acme-challenge.dabigg.com +trace
Output (sanitized slightly):
; <<>> DiG 9.10.6 <<>> TXT _acme-challenge.dabigg.com @8.8.8.8
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
...
;; ANSWER SECTION:
_acme-challenge.dabigg.com. 60 IN TXT "328JJjXtZcnkYRqkscfchEPglNy46I7yOh63q6Kbcbo"
...
; <<>> DiG 9.10.6 <<>> TXT _acme-challenge.dabigg.com @1.1.1.1
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
...
;; ANSWER SECTION:
_acme-challenge.dabigg.com. 60 IN TXT "328JJjXtZcnkYRqkscfchEPglNy46I7yOh63q6Kbcbo"
...
; <<>> DiG 9.10.6 <<>> TXT _acme-challenge.dabigg.com @9.9.9.9
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
...
;; ANSWER SECTION:
_acme-challenge.dabigg.com. 60 IN TXT "328JJjXtZcnkYRqkscfchEPglNy46I7yOh63q6Kbcbo"
...
; <<>> DiG 9.10.6 <<>> TXT _acme-challenge.dabigg.com +trace
...
dabigg.com. 172800 IN NS ns1-35.azure-dns.com.
dabigg.com. 172800 IN NS ns2-35.azure-dns.net.
dabigg.com. 172800 IN NS ns3-35.azure-dns.org.
dabigg.com. 172800 IN NS ns4-35.azure-dns.info.
...
_acme-challenge.dabigg.com. 300 IN NS jh1.pgh.resolution.certaas.web-infra.io.
;; Received 108 bytes from ns4-35.azure-dns.info in 14 ms
_acme-challenge.dabigg.com. 60 IN TXT "328JJjXtZcnkYRqkscfchEPglNy46I7yOh63q6Kbcbo"
;; Received 100 bytes from jh1.pgh.resolution.certaas.web-infra.io in 20 ms
From these tests:
_acme-challenge.dabigg.comexists and returns the expected TXT.- Public resolvers (Google, Cloudflare, Quad9) all see the record with
NOERROR. - The delegation from Azure DNS to
jh1.pgh.resolution.certaas.web-infra.ioappears to be working.
Despite that, Let’s Encrypt’s secondary validation still reports SERVFAIL looking up TXT.
Final footnote: I am not using IPv6 anywhere at this point, which I do not imagine is causing the issue. A few months ago, I remember this system working properly, but it seems to have broken again.
Questions
-
Is there anything about this kind of per-label delegation (
_acme-challenge.<domain>→ my own NS) that tends to cause issues with Let’s Encrypt’s secondary resolvers? -
Are there known requirements or quirks around:
- single-NS setups (I only have
jh1listed for_acme-challenge), - EDNS behavior, or
- TCP/UDP accessibility
that could lead toSERVFAILon LE’s side even if 8.8.8.8 / 1.1.1.1 / 9.9.9.9 succeed?
- single-NS setups (I only have
-
Is there any additional debugging I can do from my side (e.g., specific
digoptions or tools) that would more closely mimic what Let’s Encrypt’s resolvers are doing during secondary validation?
Thanks!