isitdns? wiki/Troubleshooting/How to check a forwarder
Troubleshooting

➡️ How to check a forwarder

A forwarder is a server you hand certain queries to instead of looking them up yourself. If you are not sure what a forward zone is, read forward zones first.

Asks the next resolver in the path directly, taking yours out of it.

 dig @9.9.9.9 example.isitdns.net A
; <<>> DiG 9.20.11-0ubuntu0.2-Ubuntu <<>> @9.9.9.9 example.isitdns.net A
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38499
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.isitdns.net. IN A
;; ANSWER SECTION:
example.isitdns.net. 300 IN A 192.0.2.1
;; Query time: 40 msec
;; SERVER: 9.9.9.9#53(9.9.9.9) (UDP)
;; WHEN: Mon Sep 14 17:42:32 CDT 2026
;; MSG SIZE rcvd: 64

GO NOERROR and the same answer your own resolver gave

NO GO a different answer here, which means your resolver or its forwarder changed it

run this query in the dig tool →

what each part of this output means →

Step 1: is the upstream actually answering?#

Skip your own server entirely and ask the upstream directly. If this fails, nothing else matters.

dig @192.0.2.53 internal.partner.example A

Replace 192.0.2.53 with your forwarder's IP and internal.partner.example with a name inside the forwarded zone.

What you want to see: status: NOERROR and an answer.

What each failure tells you:

What comes backWhat it meansWhat to do
an answerthe upstream is fine, go to step 2move on
REFUSEDthe upstream will not do this for youit is not a recursive resolver, or it does not host the zone, or its access list excludes your server's IP
SERVFAILthe upstream tried and failedthe problem is upstream of your upstream. Re-run without +short and read any EDE: line: it names the fault for you (extended-dns-errors)
timed outno replywrong IP, the box is down, or a firewall is blocking port 53

The target has to be recursive, or authoritative for the names you forward (RFC 1034 §4.3.2).

Step 2: is your own server actually forwarding?#

Now ask your resolver for the same name:

dig @10.0.0.53 internal.partner.example A

Replace 10.0.0.53 with your own resolver's IP.

What you want to see: the same answer the upstream gave you in step 1, and importantly, no aa flag.

;; flags: qr rd ra; QUERY: 1, ANSWER: 1, ...

Why no aa: your server did not answer from its own data, it passed the question along and relayed what came back. aa means "I own this zone" (RFC 1035 §4.1.1), and a forwarding server does not own the zone. On a working forward, aa is always absent, even when the upstream itself was authoritative, because the forwarding server strips it.

If you see aa=1, your zone classification is wrong. Your server thinks it owns this zone rather than forwarding it. You have accidentally created an auth zone. That is the bug.

If you get NXDOMAIN from your server but a real answer from the upstream, your forward zone is not matching. The query never got forwarded. Check the zone name for a typo, and remember you cannot use a wildcard: you forward internal.partner.example and everything underneath follows automatically. *.internal.partner.example is not a valid forward zone name.

Step 3: does it fail the way you want?#

When the forwarder is unreachable, your resolver does one of two things, and the default disagrees between implementations:

  • BIND defaults to forward first: try the forwarder, and if that does not work, go resolve it from the public root servers yourself (BIND 9 ARM)
  • Unbound defaults the other way: forward-first is no, so a failed forward stays failed (unbound.conf(5))

For an internal namespace, falling back is almost always wrong. If the forwarders for internal.partner.example are down, you want SERVFAIL, not a fallback query that finds nothing at the public roots and caches NXDOMAIN. That caches the outage: the name stays broken even after the forwarder comes back, until the negative-cache entry expires.

To test it: make the forwarder unreachable on purpose (block it at the firewall, or point the zone at an address with nothing on it), then ask your resolver for a name in that zone.

dig @10.0.0.53 internal.partner.example A
What comes backVerdict
SERVFAILcorrect. It failed closed, exactly as an internal namespace should
NXDOMAINwrong. It fell back to the public internet, did not find the name there, and is about to cache that

The fix is to set forwarders-only explicitly on every internal-namespace forward zone, even where the default already agrees, so the config states a decision rather than an accident. BIND: forward only;. Unbound: forward-first: no.

Step 4: is DNSSEC rejecting the answers?#

Two different failures hide here.

If the forwarded zone is signed, your resolver sets the DO bit on its upstream queries and expects RRSIG records back (RFC 4035 §3.2.1). A forwarder that strips them turns every answer into SERVFAIL.

dig @192.0.2.53 internal.partner.example A +dnssec

If the same query direct to the upstream returns RRSIG but through your resolver does not, something in between is stripping them.

If the forwarded zone is private and unsigned, like consul. or cluster.local., the failure is the opposite. The signed public DNS tree can prove those names do not exist, so a validating resolver judges the answers bogus and returns SERVFAIL. Carve that one name out of validation rather than turning validation off entirely:

See dnssec and dnssec-troubleshooting for the full picture.

Quick reference#

SymptomCause to check first
REFUSED from the upstreamforwarder is not recursive and does not host the zone
NXDOMAIN from yours, answer from upstreamforward zone is not matching, check the name
aa=1 on the answerit is an auth zone, not a forward zone
SERVFAIL on a signed zonesignatures are being stripped in transit. EDE 10 (RRSIGs Missing) says so outright
SERVFAIL on a private zonevalidation rejecting an unsigned private namespace. Which code you get names what the resolver tripped over first: EDE 10 (RRSIGs Missing) when the answer arrives unsigned and a DS in the parent says it should not be, EDE 9 (DNSKEY Missing) when the child publishes keys that do not match that DS, EDE 6 (DNSSEC Bogus) when the resolver will only say validation failed. Carving the name out of validation clears all three
NXDOMAIN when the forwarder is downfallback to root is on, set forwarders-only
latency spikes and log noiseforwarding loop, A to B to A

See also#