# How DNS actually works

> DNS turns a name like example.com into an IP address by asking a chain of servers, each of which either holds the answer or knows who to ask next. Every answer carries a TTL, a number of seconds it may be kept, which is why a record you have already changed keeps returning the old value until other people's copies expire.

- Source: https://techdecoded.dev/blog/how-dns-works
- Author: Himesh Rupareliya (https://www.linkedin.com/in/himeshrupareliya/)
- Published: 2026-09-01
- Category: Networking
- Level: Beginner
- Licence: CC BY 4.0. Quote freely with a link to https://techdecoded.dev/blog/how-dns-works

## Key takeaways

- Nothing in DNS is pushed. Every answer is one somebody asked for, and the only reason lookups are fast is that whoever asked before you kept a copy.
- There is no propagation. When a change has not taken effect, the question is never where the record has got to; it is which cache is still holding an old copy and when its TTL runs out.
- A TTL is set by the zone owner and obeyed by everyone downstream, so a safe record change means lowering the TTL first, waiting one full old TTL, and only then changing the value.
- Most servers in a lookup do not know the answer and are not meant to. The root and the registry hand back referrals; only the authoritative nameserver was ever told the address.
- A CNAME must be the only record at its name, which is why the bare domain cannot have one, and why providers synthesise A records there instead.

---

> **TL;DR**
> 
> DNS turns a name into an address by asking a chain of servers, most of which do not know the answer and only know who to ask next. Every answer you get back is a copy with a countdown on it. Change a record and every copy already handed out keeps the old value until its countdown reaches zero.

Ask a public DNS server for `wikipedia.org` twice, three seconds apart, and both answers carry the same address and a different number beside it:

```
103.102.166.224   ttl 23
103.102.166.224   ttl 20
```

Nothing about Wikipedia changed in those three seconds. The number is not part of the address and it is not a measurement of anything. It is how much longer the server you asked is willing to hand out the copy it is already holding. When it reaches zero, that copy is thrown away and the next person to ask causes a real lookup.

> **In plain English**
> 
> A printed timetable at a bus stop was accurate when it was printed, and it has a date on it. When the times change, the copy in your pocket keeps telling you the old ones until you go and look at the board again. Nobody comes to your house to correct it.
> 
> The cached record is the printed copy, the TTL is the date on it, and the authoritative nameserver is the board. There is no delivery step, in either direction.

## Why DNS is shaped like this

*\[Figure: Each step fixes what the last one broke, and no step puts the full list in one place. A four stage build-up of the domain name system. Stage one is a file of names and addresses kept on every machine, which fails because every new host means somebody hand-edits a file on every other computer. Stage two is a single central server everyone queries, which fails because one machine now answers for every name on earth and every new name needs that one operator's approval. Stage three is delegation: the root knows only which servers run each top level domain, the top level domain knows only which nameservers each domain nominated, and only the last server holds the actual addresses. That fixes ownership, but no single party holds the full list any more, so one question is no longer enough and a lookup becomes a walk of referrals. Stage four adds caching with an expiry time on every answer, because otherwise every page load on earth would walk back to the root. That final stage is the one that creates every DNS problem a developer will ever debug.\]*

`/etc/hosts` is still on your machine, it still works, and it still wins: if a name is in that file, your computer never asks anyone. That is useful when you want to point a domain at your own laptop, and it is a genuinely nasty thing to forget about when a colleague cannot reproduce what you are seeing.

The root servers have never heard of your domain and are not supposed to. That means there is no central copy to publish, no master database to synchronise, and nowhere for a change to be pushed out from.

## One lookup, run against the real servers

Every message below was captured from the real servers, starting at a root server’s address.

*\[Figure: Three questions. Two of the answers were not answers. A sequence of six messages resolving techdecoded.dev. The recursive resolver first asks a root server, a.root-servers.net at 198.41.0.4, for the A record. The root replies with a referral rather than an answer: dev. 172800 IN NS ns-tld1.charlestonroadregistry.com. The resolver then asks that .dev registry server for the same record, and gets a second referral: techdecoded.dev. 10800 IN NS kyle.ns.cloudflare.com and mallory.ns.cloudflare.com. The resolver asks Cloudflare, and only then gets a real answer: techdecoded.dev. 300 IN A 104.21.14.225 and 172.67.204.137. The point is that neither the root nor the .dev registry looked anything up on the resolver's behalf. Each knew only which servers to try next. Only the last server in the chain was ever told the address, by whoever edits the zone. Note also that the three responses carry very different TTLs, 172800 seconds for the root's referral, 10800 for the registry's, and 300 for the address itself, so different parts of the chain go stale at completely different speeds.\]*

The thing doing the walking is called a **recursive resolver** (The server that does the work of a lookup for you: it asks the root, follows the referrals, and hands you back a single final answer. Usually your ISP’s, your company’s, or a public one such as 1.1.1.1 or 8.8.8.8.), and it is almost never your own machine. Your laptop asks one server a single question and gets one answer, which is why a lookup looks atomic from where you are standing.

Only the last server in that chain is **authoritative** (Holding the real records for a name rather than a copy of somebody else’s answer. An authoritative nameserver answers from the zone it was given, and it is the only place a change can be made.). The records it holds for one domain, managed together as a unit, are that domain’s **zone** (Every record for a domain, kept and served as one set: the bare domain, the subdomains, the mail records, all of it. It is what a DNS provider’s control panel is editing.).

The root’s referral is good for 172,800 seconds, which is two days. The registry’s is good for three hours. The address itself is good for five minutes. Parts of the chain that rarely change are cached for a long time, and the part you edit is cached briefly, which is a choice somebody made rather than a property of the network.

There are 13 root server addresses, lettered a through m, operated by 12 organisations, and [a little over 2,000 server instances](https://root-servers.org/) answer on those 13 addresses. “The root” is a set of addresses, not a set of computers.

## What a record actually is

*\[Figure: The expiry is a field in the record, not something a cache added later. An A record broken into its five fields: techdecoded.dev. 300 IN A 104.21.14.225. The name is techdecoded.dev with a trailing dot, and that dot is the root of the whole tree, which every name really ends with even though tools let you omit it. The TTL is 300, meaning any party that receives this record may keep using it for 300 seconds. The class is IN for Internet, a field that survives from 1987 and carries no useful information today. The type is A, meaning the value is an IPv4 address. The value is 104.21.14.225. The important field is the TTL, because it is chosen by whoever owns the zone and obeyed by every cache downstream, which means the zone owner decides in advance how long a future change will take to become visible.\]*

A record sitting at a TTL of 86,400 has already committed you to a day of waiting before you have decided to change anything, and there is nothing you can do at the moment of the change to shorten it. You pick that number long before you need it, usually without noticing you are making a decision at all.

## The record types you will actually meet

Type

Value looks like

What it is for

`A`

`104.21.14.225`

An IPv4 address for this name.

`AAAA`

`2606:4700:3032::6815:ee1`

An IPv6 address for this name.

`CNAME`

`github.com.`

This name is an alias. Go and look up that one instead.

`MX`

`10 inbound-smtp.us-east-1.amazonaws.com.`

Where to deliver mail for this domain. The number is a priority, lowest first.

`TXT`

`"google-site-verification=GiDb..."`

Arbitrary text. Used for domain ownership proofs and mail policy.

`NS`

`kyle.ns.cloudflare.com.`

Which nameservers are authoritative for this name.

A `CNAME` is the one with a rule attached. Look at what a real one returns:

```
www.github.com.  2417  IN  CNAME  github.com.
github.com.        40  IN  A      20.207.73.82
```

The resolver asked for an address, got told “that name is really this other name”, and went and resolved that instead. You get both records back because it did both lookups.

The rule is that a `CNAME` must be the only data at its name. [RFC 1034](https://www.rfc-editor.org/rfc/rfc1034.html) puts it plainly: “If a CNAME RR is present at a node, no other data should be present.” An alias is a redirection of the entire name, so anything else sitting there would be contradicted.

That collides with the **zone apex** (The bare domain itself, example.com rather than www.example.com. It is the top of the zone you control, and it is required to carry SOA and NS records.). The apex has to carry an `NS` record, because that is how anybody finds your nameservers at all, and an `SOA` record. Both of those are data. So a `CNAME` cannot sit there, and every DNS provider’s control panel will refuse you.

Providers sell a way around it under names like `ALIAS`, `ANAME` or CNAME flattening. You type a hostname; their nameserver resolves it itself and answers queries with ordinary `A` and `AAAA` records. No resolver ever sees a `CNAME` at your apex, so no rule is broken and your `MX` and `TXT` records keep working. [Cloudflare’s flattening](https://developers.cloudflare.com/dns/cname-flattening/) and [Route 53’s alias records](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-choosing-alias-non-alias.html) are the two you are most likely to meet.

**Check yourself:** You want example.com (no www) to point at a CDN hostname like d1234.cloudfront.net. Your provider refuses to save a CNAME on the bare domain. Why?

-   **(correct answer)** The bare domain already has NS and SOA records, and a CNAME must be the only record at its name
    -   Right. RFC 1034 says a CNAME cannot coexist with other data at the same name, and the apex is required to carry NS and SOA. Providers get around it with ALIAS, ANAME or CNAME flattening, where they resolve the target themselves and answer with plain A records.
-   CNAMEs only work on subdomains, because resolvers strip the leading label
    -   Resolvers do not strip labels, and there is nothing special about a subdomain to a resolver. The restriction is not about position in the name, it is about a CNAME being unable to share a name with any other record. A subdomain usually has no other records, which is the only reason it works there.
-   The CDN would have to be given control of your whole domain
    -   A CNAME hands over resolution of one name, not the domain. Control of the domain is what an NS record delegates. This one is about a record coexistence rule, not about trust.

## Who is holding a copy

*\[Figure: A lookup stops at the first layer holding a copy that has not expired. Four layers a DNS lookup passes through, top to bottom. First the browser's own DNS cache, which is separate from the operating system's and is why a page can keep reaching an old address after you flushed everything else. Second the operating system's stub resolver cache, shared by every program on the machine. Third the recursive resolver, run by an ISP, a company, or a public service such as 1.1.1.1, and shared by thousands of people, which makes its copy the one most likely to be stale for somebody who is not you. Fourth and last, the authoritative nameserver, the only party in the list that is not holding a copy of somebody else's answer. A lookup stops at the first layer with an unexpired copy and never reaches the layers below it. That is why two people on the same office network can get different answers at the same moment and both be behaving correctly, and why a query tool and a browser on one machine can disagree.\]*

You can clear the top two layers yourself and you cannot touch the third. That asymmetry is what makes “it works on my machine” so common here: flushing your browser and your operating system fixes your own view and changes nothing for anybody else, including the resolver you and your colleague share.

**Check yourself:** You change your site's A record at 14:00. The record's TTL was 3600. At 14:30 a colleague in another country still loads the old site. What is happening?

-   **(correct answer)** A resolver in their path cached the old record before 14:00 and will keep serving it until its copy expires
    -   Right. Their resolver was handed a copy stamped with 3600 seconds and it is honouring that. Nothing is travelling anywhere, and nothing is out of sync. The only variable is when that particular copy was fetched, so the worst case is one full TTL after your change.
-   The change is still propagating outward from your DNS host to other DNS servers
    -   This is the common picture, and it describes something that does not happen. No DNS server sends records to any other. Your change is instantly true at your nameserver, and every other machine finds out only when it next asks, which is when the copy it already holds expires.
-   Their country's DNS infrastructure updates on a slower schedule
    -   There is no schedule and no per-country infrastructure to be on one. Distance affects how long a lookup takes, not when a cached copy expires. A colleague at the next desk could see the same thing if their resolver happened to cache the old record a minute before you made the change.

## Changing a record without a wait

Since the wait is other people’s copies timing out, you can remove it by making those copies short-lived before you touch anything that matters.

*\[Figure: The change takes a second. The two waits either side of it are the work. A five step timeline of a safe DNS record change, with the TTL value drawn as a bar at each step. At 09:00 you lower the record's TTL from 3600 seconds to 60, and change nothing else. This is invisible to the world, because caches are still holding copies stamped 3600 and will honour that number. At 10:00, one full old TTL later, the last of those copies has expired and been refetched, so every cache is now holding a record that says 60. At that point you change the address itself. By 10:01, one new TTL later, nothing anywhere is serving the old address. At 11:00 you put the TTL back to 3600, because a 60 second TTL means every cache re-asks your nameservers every minute, which is fine for an afternoon and wasteful forever. The total wait is one old TTL plus one new TTL, and it is entirely predictable in advance. Lowering the TTL at the same moment as the change buys nothing, because the copies already in circulation were stamped with the old number.\]*

If you did not plan ahead, there is no button to press: you wait one old TTL. Anyone telling you to allow 24 to 48 hours is quoting folklore from an era of much longer default TTLs, and you can check the real figure by asking for the record and reading the number that comes back.

## The traps

> **The registrar and the DNS host are usually different companies**
> 
> The registrar is who you rent the name from, and the only record they control is the NS record that names your nameservers. That record does not live in your zone; it lives one level up, in the TLD’s. Your DNS host runs the nameservers it points at and holds everything else. When the world cannot find your domain at all, check the delegation before you look at any individual record, because a perfect A record on nameservers nobody has been told about is invisible.

**“Does not exist” is cached too.** Ask for a name that is not there and you do not get silence, you get a refusal with an expiry on it, called **NXDOMAIN** (“Non-existent domain”, the response code a nameserver returns when it is authoritative for the zone and the name is genuinely not in it. Distinct from a lookup that failed or timed out.):

```
nope-not-here.techdecoded.dev.  NXDOMAIN
techdecoded.dev.  1800  IN  SOA  kyle.ns.cloudflare.com. dns.cloudflare.com.
                                 2413665214 10000 2400 604800 1800
```

That trailing `1800` in the `SOA` record is what caps how long resolvers may remember the absence, a mechanism defined in [RFC 2308](https://www.rfc-editor.org/rfc/rfc2308.html). So if you or anyone else looked up a subdomain before you created it, the negative answer is sitting in a cache and your brand new record will not be seen until it expires. This catches people setting up certificate validation records, where the tooling checks for the record, fails, and then keeps failing for half an hour after you have added it.

**The TTL is per record, not per domain.** On this site’s zone right now the `A` record has a TTL of 300, the `MX` has 3600, and the `NS` has 86400. Changing where mail goes and changing where the website points are two different waits.

**`/etc/hosts` is not DNS and does not care about any of this.** It is consulted before a lookup happens, so a stale line in it will survive every cache flush and every TTL you have ever set.

**Check yourself:** You look up api.example.com, get NXDOMAIN, then immediately create the record with a TTL of 60. You look it up again a minute later and still get NXDOMAIN. Why?

-   **(correct answer)** The resolver cached the 'does not exist' answer, and that cache entry has its own expiry taken from the zone's SOA record
    -   Right. Negative answers are cached like positive ones, and the length comes from the SOA, not from the TTL on the record you have created. Your new record's TTL is irrelevant, because no resolver has fetched it yet.
-   New records take a few minutes to become active on the nameserver
    -   The record is live at your authoritative nameserver the moment it is saved, and asking that nameserver directly will prove it. The delay is entirely in the resolver between you and it, holding the earlier negative answer.
-   The TTL of 60 has not elapsed yet, so the record is not being served
    -   A TTL is not a delay before a record activates. It is a limit on how long somebody may keep a copy after receiving one. It only starts mattering once a resolver has actually fetched the record.

## What DNS will not do for you

**It does not route anything.** Handing back an address is the end of the job. What happens to the packet afterwards is routing, and DNS has no involvement in it.

**It does not know whether your server is up.** A nameserver returns what the zone says, whether or not anything is listening at that address. Health checking is a product some managed DNS providers sell on top, where their systems probe your servers and edit the zone for you. Plain DNS has no concept of up or down.

**It is a poor load balancer.** You can put several addresses on one name, and clients end up spread across them, but caching means you cannot control which client gets which address or shift traffic away from one quickly when you need to.

**It is not private and it is not authenticated.** Classic DNS is unencrypted, usually over UDP port 53, so the names you look up are readable by anything on the path, and anyone positioned to reply before the real nameserver does can lie to you. DNS over HTTPS and DNS over TLS address the first problem, DNSSEC addresses the second, and both are separate topics. Worth knowing when reading [how SSL actually works](/blog/how-ssl-works): HTTPS hides the page you asked for, but the DNS lookup that preceded it announced the hostname in the clear.

## Checking your work

Ask your own machine what it resolves a name to, and how it got there:

```bash
resolvectl query techdecoded.dev
```

```
techdecoded.dev: 104.21.14.225
                 172.67.204.137
                 2606:4700:3032::6815:ee1
                 2606:4700:3030::ac43:cc89

-- Information acquired via protocol DNS in 60.7ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network
```

That gives you addresses but not the TTL. To see the countdown, ask a resolver over HTTPS, which returns the raw fields as JSON:

```bash
curl -sH 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=techdecoded.dev&type=A'
```

```
{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,
 "Question":[{"name":"techdecoded.dev","type":1}],
 "Answer":[{"name":"techdecoded.dev","type":1,"TTL":300,"data":"104.21.14.225"},
           {"name":"techdecoded.dev","type":1,"TTL":300,"data":"172.67.204.137"}]}
```

The tool most guides reach for here is `dig`, which comes in the `dnsutils` package on Debian and Ubuntu and `bind-utils` on Fedora and RHEL. It prints the same fields in the classic zone-file layout, and `dig +trace` walks the chain from the root the way the sequence above does.

### Try it yourself: Watch a TTL run out

Ask for the same name twice in a row and compare the `TTL` field:

```bash
curl -sH 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=wikipedia.org&type=A'
# wait a few seconds, then run it again
```

Then ask for a name that does not exist, and look at what comes back instead of an address:

```bash
curl -sH 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=nope-not-here.wikipedia.org&type=A'
```

**What you should see**

The `TTL` in the second lookup is lower by roughly the number of seconds you waited. You are watching one resolver’s copy age. If it jumps back up instead, you reached a different machine behind the same address, holding its own copy on its own clock.

The name that does not exist returns `"Status": 3`, which is `NXDOMAIN`, and an `Authority` section containing an `SOA` record rather than an `Answer` section. The last number in that `SOA` is how many seconds resolvers may remember that this name is missing. That is the number standing between you and a record you are about to create.

## Where to go next

Point a domain at a CDN and you are making a DNS change and nothing else, which is worth reading alongside [what a CDN actually does](/blog/what-is-a-cdn): the cache key section there is the same “who is holding a copy” reasoning one layer up the stack.

The specifications are unusually readable for their age. [RFC 1034](https://www.rfc-editor.org/rfc/rfc1034.html) is the concepts, published in November 1987 and still accurate. [RFC 2308](https://www.rfc-editor.org/rfc/rfc2308.html) is negative caching, and it is short.

The obvious next question is how you know an answer was not tampered with in transit, given that anyone who can reply faster than the real nameserver wins. That is DNSSEC, and it deserves its own article.

## Quick answers

**How long does DNS propagation take?**

There is no propagation, so there is nothing to wait for except caches expiring. A resolver holding a copy will keep serving it for exactly the TTL that copy was handed, then re-ask. If the record had a TTL of 3600 when it was cached, the longest anyone can be stuck on the old value is one hour, no matter what your DNS provider's control panel says about 24 or 48 hours.

**Why does dig show the new record but my browser still loads the old site?**

Because they are asking different caches. A query tool usually goes straight to a resolver, while a browser checks its own DNS cache first, then the operating system's, then the resolver. Any of those can still be holding an unexpired copy. Browsers also keep HTTP connections alive and cache the page itself, so even a correct lookup does not guarantee a fresh request.

**What is the difference between my registrar and my DNS host?**

The registrar is who you rent the name from, and the only DNS record they control is the NS record that says which nameservers are in charge. That record lives in the TLD's zone, one level above yours. The DNS host runs those nameservers and holds everything else: your A, MX, TXT and the rest. If the world cannot find your domain at all, check the NS delegation at the registrar before you look at any individual record.

**Why can I not put a CNAME on my bare domain?**

RFC 1034 says that if a CNAME is present at a name, no other data may be present at that name. The bare domain necessarily carries SOA and NS records, so a CNAME cannot legally sit alongside them. Providers work around it with names like ALIAS, ANAME or CNAME flattening: you type a hostname, and their nameserver resolves it and answers with plain A and AAAA records, so no resolver ever sees a CNAME there.

**Does DNS know when my server goes down?**

No. A nameserver hands back whatever the zone says, and it has no idea whether anything is listening at that address. Managed DNS providers sell health checking as a product layered on top, where their own systems probe your servers and edit the zone for you, but plain DNS has no concept of a server being up or down.

