Type something to search...
What is the difference between recursive and iterative DNS queries?

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 queryIterative query
What the client asks for"Give me the final answer""Give me the best answer you have"
Who follows referralsThe serverThe client
Typical clientYour device's stub resolverA recursive resolver
Typical serverA recursive resolver (ISP, public DNS, company DNS)Root, TLD and authoritative name servers
Possible responsesAnswer, NXDOMAIN, NODATA or an errorAnswer, referral, NXDOMAIN, NODATA or an error
Header flag set by clientRD (recursion desired) = 1RD = 0
Server workloadHigh: may send many queries per requestLow: 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 A record 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 NOERROR with an empty answer section).
  • An error, such as SERVFAIL if the resolver couldn't get a valid answer, or REFUSED if 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 by dig and 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.
  • NS records 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:

  1. Your device sends a recursive query to the resolver.
  2. The resolver sends an iterative query to a root server and gets a referral to .com.
  3. The resolver sends an iterative query to a .com server and gets a referral to example.com's name servers.
  4. The resolver sends an iterative query to an example.com name server and gets the answer.
  5. 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.

Tags :
Share :

Related Posts

What is the difference between an A record and a CNAME record?

What is the difference between an A record and a CNAME record?

The difference between an A record and a CNAME record is what they point to. An A record points a hostname directly at an IPv4 address, such

Dive Deeper
What are root name servers and how many are there?

What are root name servers and how many are there?

Root name servers are the DNS servers at the very top of the Domain Name System. They don't know the IP address of any website, but they know where t

Dive Deeper
What happens when you type a URL into your browser?

What happens when you type a URL into your browser?

When you type a URL such as https://www.example.com/pricing into your browser and press Enter, the browser first works out whether you typed an add

Dive Deeper