NxValue Stack

What Happens in the 50 ms Before Every Request: A Walkthrough of DNS Resolution

You type example.com and press Enter. Before your browser sends a single byte of HTTP, before TLS starts, before any page content exists, something else has to happen first: your device has to work out where example.com actually is.

That job is DNS, and on an uncached lookup it often takes somewhere in the region of 20 to 150 milliseconds, depending on where you are and who you ask. The "50 ms" in the title is a rough middle, not a promise. It is also a delay that stacks up: a modern web page can touch dozens of domains, and every new one starts with the same question.

This guide follows one lookup from start to finish, so you know what is happening, where the time goes, and what you can measure and control.

Step 1: Your device checks its own memory first

The fastest DNS lookup is the one that never leaves your machine. Several layers get a chance to answer before anything touches the network:

If any of these has a fresh answer, the cost is effectively zero. If not, the stub resolver sends the question out.

It is called a stub because it is deliberately simple: it does not do any real resolving. It just forwards the question to a recursive resolver.

Step 2: The question goes to a recursive resolver

Which server gets the question depends on your settings. Usually it came from DHCP, so it is your router, which forwards to your ISP's resolver. Some people point their device at a big public resolver instead. Others point it at a resolver they run themselves.

Two things matter at this hop:

  1. Distance. A resolver on your local network answers in a couple of milliseconds. One across the country does not.
  2. Transport. Classic DNS is plain text over UDP port 53, so anyone on the path can read it. DNS-over-TLS (port 853) and DNS-over-HTTPS (port 443) wrap the same question in encryption. The lookup itself is unchanged; only the envelope is.

The recursive resolver is the part that does the real work, and it has a cache of its own.

Step 3: The resolver's cache usually saves the day

When the question arrives, the resolver looks in its cache. If another user asked for the same name recently and the record has not expired, you get the answer straight away, usually in under a millisecond of processing.

Expiry is controlled by the TTL (time to live) on each record. A site that sets a TTL of 300 is telling every resolver: you may reuse this answer for five minutes. Low TTLs let site owners move traffic quickly. High TTLs mean fewer lookups and faster responses.

Caches also remember negative answers. If you look up a name that does not exist, that "no such domain" result is cached too, for a period set by the zone's SOA record, so a typo does not trigger a full search every time.

Most of the time, this is where the story ends. The harder case is a cache miss.

Step 4: The walk, from the root down

On a miss, a recursive resolver starts at the top and works down, one referral at a time. For www.example.com it goes roughly like this:

  1. Ask a root server: "Where is www.example.com?" The root does not know, but it knows who runs .com and points you to those servers.
  2. Ask a .com server. It does not know either, but it knows which name servers are responsible for example.com, and returns them.
  3. Ask the authoritative server for example.com. This one holds the real record and returns the address.

You can watch this happen with dig +trace. This is trimmed and the address is a documentation placeholder, but the shape is real:

$ dig +trace www.example.com

.                518400  IN  NS   a.root-servers.net.
...
com.             172800  IN  NS   a.gtld-servers.net.
...
example.com.     172800  IN  NS   a.iana-servers.net.
...
www.example.com. 300     IN  A    203.0.113.10

Note the TTLs. Root and TLD referrals are cached for a long time (days), so in practice a busy resolver almost never contacts the root servers at all. It already knows who runs .com. A "cold" lookup for a domain nobody has asked about in a while usually costs just one or two network round trips, not three or four. That is why recursive resolving is faster in real life than it looks on paper.

Two refinements are worth knowing:

Step 5: DNSSEC adds a verification step

Plain DNS has no built-in way to prove an answer is genuine. DNSSEC fixes that with a chain of signatures: each zone signs its records, and the parent zone vouches for the child's signing key, all the way up to the root.

A validating resolver checks that chain. If a signature is wrong or missing where one is required, it returns an error instead of a forged answer. When validation succeeds, the response carries the AD (authenticated data) flag.

The cost is a few extra lookups for keys and signatures (DNSKEY, DS, RRSIG records), but those are cached too, so the overhead is small after the first few queries. One honest limit: DNSSEC proves an answer is authentic. It does not make it private. That is a separate job, handled by encrypted transport.

Where filtering fits in

Everything above describes a plain resolver. A DNS filter such as Pi-hole or AdGuard Home sits in front of the resolving step, between your device and the walk.

The filter checks the requested name against your blocklists. If it matches an ad network, a tracker, or a telemetry domain, the filter answers immediately with a "nowhere" response (typically 0.0.0.0 or NXDOMAIN), and no recursive walk ever happens.

That has a side effect that surprises people: a blocked lookup is usually faster than an allowed one, because it is answered locally with no network hop at all. On a page loaded with third-party trackers, that adds up, along with the bandwidth you no longer spend downloading them.

Only the allowed names continue down to the recursive resolver.

Putting numbers on it

Here is a rough picture of where time goes. These are typical ranges, not benchmarks, and your results will differ:

The slow case is rare but memorable. Unbound has settings that smooth it out: prefetch refreshes popular records shortly before they expire, so the next request still finds a warm cache, and serve-expired can briefly answer from an expired entry while it updates in the background.

Measure it yourself

You do not have to take any of this on trust. dig shows the time for each query:

dig example.com | grep "Query time"

Run it twice. The first run may be a cache miss, and the second should be close to instant. To test a specific resolver, name it directly. For example, a local Unbound listening on port 5335:

dig @127.0.0.1 -p 5335 example.com

To see the validation result, add +dnssec and look for ad in the flags line of a signed domain.

Who sees what along the way

Each hop learns something, and the picture changes depending on who you ask:

Encrypting DNS hides your questions from the network between you and the resolver. It does not hide the IP address you then connect to, and in many cases the site name still appears in the TLS handshake. DNS privacy is one layer, not a cloak.

Running the whole chain yourself

Everything above can be assembled on a small server. A filter handles blocking, Unbound handles recursion and DNSSEC validation, and an encrypted endpoint carries the queries from your devices. We walk through the self-built version in Pi-hole with Unbound, and in Encrypted DNS Explained for the DoH and DoT side.

It is a good weekend project, and it is also a lot of small pieces to keep patched and certified. If you would rather skip that part, NxValue Stack runs this exact chain for you: your own isolated node with Pi-hole or AdGuard Home in front of Unbound, plus DoH and DoT endpoints already set up. It is billed hourly (the BASE-N plan is $4/month) and you can delete it at any time. You can look at a live dashboard in the demo node. It is shared publicly and meant for testing only, not personal use.

Either route gives you the same architecture. The difference is who does the maintenance.

Some limits to know about

Summary

Pi-hole is a registered trademark of Pi-hole LLC. AdGuard Home is a trademark of AdGuard Software Limited. NxValue Stack is an independent service provider and is not affiliated with or sponsored by either project.

Skip the setup

Get your own private Pi-hole node with DoH and DoT endpoints. Billed hourly, cancel anytime.