
What is the difference between recursive and iterative DNS queries?
The difference between recursive and iterative DNS queries is who does the work of finding the answer. In a recursive query, the client asks a DNS server to return the complete, final answer, and the server must go and find it, however many other servers it has to contact. In an iterative query, the client asks a server for the best answer it already has, and the server replies either with the answer or with a referral pointing to another server that's closer to it. The client then follows that referral itself. Your device sends recursive queries to its resolver, and the resolver sends iterative queries to the root, TLD and authoritative name servers.
Both types of query use exactly the same DNS message format. The only thing that changes is a single bit in the header and how the server chooses to respond. In this article you'll learn how each type works, which flags control them, how to perform each one by hand with dig, how to write a tiny iterative resolver in Python, and why the distinction matters for security and server configuration.
The Short Version
| Recursive query | Iterative query | |
|---|---|---|
| What the client asks for | "Give me the final answer" | "Give me the best answer you have" |
| Who follows referrals | The server | The client |
| Typical client | Your device's stub resolver | A recursive resolver |
| Typical server | A recursive resolver (ISP, public DNS, company DNS) | Root, TLD and authoritative name servers |
| Possible responses | Answer, NXDOMAIN, NODATA or an error | Answer, referral, NXDOMAIN, NODATA or an error |
| Header flag set by client | RD (recursion desired) = 1 | RD = 0 |
| Server workload | High: may send many queries per request | Low: answers only from its own data |
How a Recursive Query Works
When you open a website, your operating system's stub resolver sends a query to the resolver configured on your network. That query has the recursion desired (RD) flag set. It's effectively saying: "I don't want referrals. Go and find the answer and come back when you have it."
The resolver now takes full responsibility. It might answer straight from its cache, or it might have to contact several other servers first. Either way, the client receives just one of these outcomes:
- The answer, such as an
Arecord with an IP address. - NXDOMAIN, meaning the name doesn't exist.
- NODATA, meaning the name exists but has no records of the requested type (shown as
NOERRORwith an empty answer section). - An error, such as
SERVFAILif the resolver couldn't get a valid answer, orREFUSEDif it won't serve you.
The client never sees the intermediate steps. That's the whole point: stub resolvers are deliberately simple, and recursion lets them stay that way.
You can see the flags involved with dig, which sends recursive queries by default:
dig @1.1.1.1 www.example.com A
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40112
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
www.example.com. 300 IN A 203.0.113.10
rd(recursion desired) was set bydigand copied into the reply.ra(recursion available) was set by the server, confirming it's willing to recurse for you.
How an Iterative Query Works
An iterative query (sometimes called a non-recursive query) leaves the RD flag clear. The server answers only from the data it holds, without contacting anyone else. If it's authoritative for the name, it returns the answer. If it isn't, but it knows which servers are responsible for a closer part of the name, it returns a referral.
A referral is a response with:
- An empty answer section.
NSrecords in the authority section, naming the servers for the next zone down.- Often glue records in the additional section, giving the IP addresses of those name servers.
The client reads the referral, picks one of the listed servers, and asks again. It repeats this until it reaches a server that can answer.
This is exactly how a recursive resolver resolves a name it doesn't have cached. A complete lookup for www.example.com therefore combines both types:
- Your device sends a recursive query to the resolver.
- The resolver sends an iterative query to a root server and gets a referral to
.com. - The resolver sends an iterative query to a
.comserver and gets a referral toexample.com's name servers. - The resolver sends an iterative query to an
example.comname server and gets the answer. - The resolver returns that answer to your device, completing the recursive query.
Root and TLD servers only ever answer iteratively. They handle enormous query volumes, and they couldn't possibly chase answers on behalf of every resolver on the internet.
Performing Iterative Queries by Hand With dig
The best way to understand iteration is to do it yourself. The +norecurse option tells dig to clear the RD flag.
Step 1: Ask a Root Server
dig @a.root-servers.net www.example.com A +norecurse
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22233
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 27
;; QUESTION SECTION:
;www.example.com. IN A
;; AUTHORITY SECTION:
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
;; ADDITIONAL SECTION:
a.gtld-servers.net. 172800 IN A 192.5.6.30
b.gtld-servers.net. 172800 IN A 192.33.14.30
This is a referral. There's no answer, no aa flag and no ra flag. The root has simply told you who handles .com and, helpfully, their addresses.
Step 2: Follow the Referral to .com
dig @a.gtld-servers.net www.example.com A +norecurse
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 3
;; AUTHORITY SECTION:
example.com. 172800 IN NS ns1.example.net.
example.com. 172800 IN NS ns2.example.net.
Another referral, this time to the domain's own name servers. Because those servers are in a different domain (example.net), the .com servers may not include their addresses, so a real resolver would look them up separately.
Step 3: Ask the Authoritative Server
dig @ns1.example.net www.example.com A +norecurse
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
www.example.com. 300 IN A 203.0.113.10
The aa (authoritative answer) flag confirms this came from a server responsible for the zone. You've just done by hand what a resolver does in a few milliseconds.
Letting dig Do the Iteration: +trace
dig +trace automates the same process. It starts at the root and follows each referral, printing every step:
dig +trace www.example.com A
One subtlety: +trace asks your configured resolver for the root server list at the start, then iterates on its own from there. It's a quick way to see where in the chain a lookup breaks.
What Happens If You Ask a Root Server to Recurse?
If you leave RD set when querying a server that doesn't offer recursion, it still answers iteratively, and dig warns you:
dig @a.root-servers.net com NS
;; WARNING: recursion requested but not available
The server ignored the request for recursion and returned a referral anyway.
A Tiny Iterative Resolver in Python
To make the process concrete, here's a minimal iterative resolver using the dnspython library. It starts at a root server, clears the RD flag, and follows referrals until it gets an answer. Install the library first:
python3 -m pip install dnspython
Save this as iterate.py:
import sys
import dns.flags
import dns.message
import dns.query
import dns.rcode
import dns.rdatatype
import dns.resolver
ROOT_SERVER = "198.41.0.4" # a.root-servers.net
def iterate(name, rdtype="A"):
server = ROOT_SERVER
for step in range(1, 11):
query = dns.message.make_query(name, rdtype, use_edns=0, payload=1232)
query.flags &= ~dns.flags.RD # ask for an iterative answer
response = dns.query.udp(query, server, timeout=3)
print(f"step {step}: asked {server}")
if response.answer:
for rrset in response.answer:
print(f" answer: {rrset}")
return
ns_names = [
rr.target.to_text()
for rrset in response.authority
if rrset.rdtype == dns.rdatatype.NS
for rr in rrset
]
if not ns_names:
print(f" no answer and no referral ({dns.rcode.to_text(response.rcode())})")
return
glue = [
rr.address
for rrset in response.additional
if rrset.rdtype == dns.rdatatype.A
for rr in rrset
]
zone = next(r.name for r in response.authority if r.rdtype == dns.rdatatype.NS)
print(f" referral to {zone} -> {ns_names[0]}")
if glue:
server = glue[0]
else:
# No glue: look up the name server's address separately
server = dns.resolver.resolve(ns_names[0], "A")[0].address
print("gave up after too many referrals")
if __name__ == "__main__":
iterate(sys.argv[1] if len(sys.argv) > 1 else "www.example.com")
Run it:
python3 iterate.py www.example.com
step 1: asked 198.41.0.4
referral to com. -> l.gtld-servers.net.
step 2: asked 192.41.162.30
referral to example.com. -> ns1.example.net.
step 3: asked 198.51.100.53
answer: www.example.com. 300 IN A 203.0.113.10
For a name that doesn't exist, the final server returns NXDOMAIN and the script stops. Real resolvers do far more than this: they cache every step, retry other servers on timeouts, follow CNAME chains, fall back to TCP for large answers, validate DNSSEC and protect against spoofing. Notice that when there's no glue, this script cheats by using the system resolver to find the name server's address. A real resolver would start a fresh iterative lookup for that name instead.
Why the Distinction Matters
Workload and Caching
Recursion is expensive. A single recursive query can trigger several outbound queries, each with its own network round trip. Resolvers make this affordable by caching aggressively, so most recursive queries are answered without any iteration at all. Authoritative servers, on the other hand, can answer iterative queries from memory without contacting anyone, which is why they scale to huge volumes.
Security: Open Resolvers
A server that accepts recursive queries from anyone on the internet is called an open resolver. Open resolvers are routinely abused in DNS amplification attacks, where attackers send small spoofed queries that produce large responses aimed at a victim. They're also easier targets for cache poisoning.
The rule of thumb is simple:
- Authoritative servers should answer iterative queries from anyone, but never offer recursion.
- Recursive resolvers should offer recursion only to the clients they're meant to serve.
In BIND, an authoritative-only server should disable recursion completely:
options {
directory "/var/cache/bind";
recursion no;
allow-query { any; };
};
A resolver for an internal network should restrict who can use it:
acl "trusted" {
127.0.0.0/8;
192.0.2.0/24;
2001:db8:1::/48;
};
options {
directory "/var/cache/bind";
recursion yes;
allow-recursion { trusted; };
allow-query-cache { trusted; };
};
In Unbound, the equivalent is access-control:
server:
access-control: 192.0.2.0/24 allow
access-control: 0.0.0.0/0 refuse
You can check whether a server offers recursion to you by sending a query for a domain it doesn't host and looking at the flags and status:
dig @198.51.100.53 www.example.org A
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 1734
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
REFUSED and no ra flag is what you want to see from an authoritative-only server.
Troubleshooting
Knowing the difference helps you debug. If a recursive query to your resolver fails, iterate by hand with +norecurse or use +trace to find the exact step where things go wrong. A referral to name servers that don't respond, or that respond without the aa flag, points to a broken delegation rather than a resolver problem.
Recursive vs Iterative vs Forwarding
You'll sometimes see a third term, forwarding. A forwarder receives a recursive query and, instead of iterating itself, sends its own recursive query to another resolver. Home routers do this: they forward your queries to your ISP's resolver. From the protocol's point of view, a forwarder is just a client sending recursive queries upstream, often with a cache in the middle.
FAQ: Recursive and Iterative DNS Queries
From the client's point of view, a recursive query is easier and usually faster, because the resolver probably has the answer cached. A full iterative lookup from the root takes several round trips. In practice the two always work together.
Browsers and operating systems send recursive queries to a resolver. They rely on the resolver to do the iteration. Only resolvers and diagnostic tools normally send iterative queries.
RD stands for recursion desired. A client sets it to ask the server to find the full answer. If the server is willing, it sets the RA (recursion available) flag in its reply. If RD is clear, the query is iterative.
Yes, in everyday usage the terms mean the same thing: a query sent with the RD flag clear, which the server answers only from its own data, returning either an answer or a referral.
Root servers receive enormous volumes of traffic. Recursion would require them to contact other servers and cache results for every query, which would be slow, expensive and a security risk. Returning a referral is quick and stateless.
From a machine outside your network, query your server for a domain it doesn't host, such as dig @your-server-ip www.example.org. If you get an answer with the ra flag set, it's offering recursion to the world and should be restricted.
Conclusion
Recursive and iterative queries are two halves of the same process. A recursive query hands the whole job to a resolver, which promises to come back with a final answer. An iterative query asks only for what a server already knows, and the asker follows referrals down from the root to the authoritative name server. Your device uses the first kind, your resolver uses the second, and a single bit, RD, tells servers which is wanted.
Try the +norecurse walk-through on a domain you manage, and you'll see the delegation chain that every lookup depends on. Then make sure your own servers behave correctly: authoritative servers should never recurse, and resolvers should recurse only for the networks they serve.


