Network
DNS records explained: A, AAAA, CNAME, MX, TXT and NS
Most DNS trouble is a mismatch, not a mystery: a record type used where the standard forbids it, a hostname that was never fully qualified, or a change judged before old answers had time to expire. This article follows a lookup from the operating system to the authoritative server, explains what each common record type is for, covers the CNAME rules that surprise people, and shows how the MaffeiTools DNS tools read the same answers, including what they cannot see.
By the MaffeiTools team · Updated · 10 min read
How a lookup travels: stub, recursive resolver, and three tiers of servers
An application never walks the DNS tree itself. It calls the operating system's stub resolver, which holds the addresses of one or more recursive resolvers and forwards the question. RFC 1034 describes this as moving resolution out of the local machine into a name server that supports recursive queries, leaving the stub with just a list of server addresses.
The recursive resolver does the work. On a cold cache it asks a root server for www.example.com. The root does not know, but it answers with a referral to the servers for the com zone. Those servers do not know either, and refer the resolver to the servers named in the NS records for example.com. Those are the authoritative servers, which hold the zone data and give the real answer. Every reply, referrals included, is cached for its TTL, so a warm resolver usually skips straight to the last step.
There is no single database called DNS to update. You edit a zone on the authoritative servers, and every recursive resolver keeps its own independently timed copy of whatever it last fetched, so the answer you see depends on which resolver you asked. That is the whole basis of what people call propagation.
The record types you will actually edit
Each record has an owner name, a type, a TTL and a value. Ten types cover almost every real configuration.
- A maps a name to a 32-bit IPv4 address. AAAA (RFC 3596) maps it to a 128-bit IPv6 address. A name may have several of either, and resolvers may vary the order of the set.
- CNAME makes a name an alias of another name. The resolver restarts the lookup at the target and returns the whole chain.
- NS names the authoritative servers for a zone. The value must be a host with address records, never an alias (RFC 2181 section 10.3). The delegating NS set lives in the parent zone and is changed through your registrar.
- SOA appears once per zone, at the apex: primary server name, responsible mailbox, serial, refresh, retry, expire and a minimum. RFC 1035 defined the minimum as a floor on record TTLs, and RFC 2308 later redefined its effective role as the cache time for negative answers.
- MX lists mail exchangers, each with a preference number where lower is tried first.
- TXT holds text in strings of at most 255 bytes each, because each string has a one-byte length prefix. Longer values are split into several strings that consumers join without separators. SPF, DKIM keys, DMARC policy and site-verification tokens all live in TXT.
- PTR maps an address back to a name, in the in-addr.arpa tree for IPv4 (192.0.2.10 becomes 10.2.0.192.in-addr.arpa) or ip6.arpa for IPv6. It is published by whoever controls the IP block, usually the hosting provider, not by the zone that owns the forward name.
- CAA (RFC 8659) states which certificate authorities may issue for a name, for example: 0 issue "ca.example.net". A compliant CA must check before issuing, starting at the requested name and climbing toward the root until it finds a CAA record set.
- SRV (RFC 2782) locates a service at _service._proto.name, giving priority, weight, port and target host. Clients try the lowest priority first and use weight within a priority.
Why a CNAME cannot share its name with anything, and why that rules out the apex
RFC 2181 section 10.1 states the rule as an exhaustive list. For any name in the DNS, exactly one of these holds: one CNAME exists (optionally with DNSSEC records), or one or more records exist and none is a CNAME, or the name exists with no records, or the name does not exist. A CNAME means every question about this name is answered at the target. An A, MX or TXT record beside it would contradict that, so the combination is invalid.
The zone apex, the bare example.com, must carry the zone's SOA record (RFC 1035 expects exactly one at the top of the zone) and, in practice, its NS records, so a CNAME there always collides with them. The same collision happens at any subdomain that also needs MX or TXT records. It bites when a hosting platform hands you a hostname instead of an IP address and you want the bare domain to point at it.
The compliant options are A and AAAA records pointing at addresses the platform publishes, or a provider feature usually named ALIAS, ANAME or CNAME flattening. Those features are not a record type: the provider's servers resolve the target themselves and answer with ordinary A and AAAA records, so lookup tools show addresses, not an alias.
Worth knowing. A DNS host that lets you save a CNAME next to other records at the same name has given you an invalid zone. Resolvers handle it inconsistently, so treat it as broken even when a test appears to work.
MX priority, the implicit MX, and the null MX
Each MX record carries a 16-bit preference and an exchange host. Lower preference is tried first (RFC 1035). Senders must randomize among records that share a preference, to spread load (RFC 5321). The MX record lookup tool flags a duplicate preference as a warning because it is often a typo where a primary and a backup were intended.
RFC 5321 section 5.1 governs what senders do next. If a domain has no MX records at all, the sender treats it as having an implicit MX with preference 0 pointing at the domain itself, so mail is attempted at whatever the A or AAAA record says. If MX records exist but none is usable, that is an error, not a cue to fall back to the A record. A domain that runs a web server and no MX will therefore get delivery attempts on the web server.
RFC 7505 adds a way to say that a domain takes no mail: a single MX record at preference 0 whose target is the root name, written as a dot. Senders can then fail immediately instead of retrying for days. The tool recognizes exactly that shape.
The exchange host must have address records and must not be an alias. RFC 2181 section 10.3 explains why: when a server answers an MX query, it appends the addresses of the target, and that step does not chase CNAMEs, so an aliased target costs extra queries on every lookup. RFC 5321 goes further and puts a name that resolves to a CNAME outside the standard. The tool checks each exchange for a CNAME alongside its A and AAAA lookups and reports one as an error.
input: mail1.example.com pref 10 (192.0.2.25)
mail2.example.com pref 10 (192.0.2.25)
alias.example.com pref 20 (no addresses, is a CNAME)
issues: ["MX points at a CNAME: alias.example.com","Duplicate priority 10"]
input: "." pref 0
issues: ["Null MX (RFC 7505)"]
input: (no MX records)
issues: ["No MX records"]TTL, negative caching, and what propagation really is
A TTL is a number of seconds, an unsigned value with a maximum of 2,147,483,647 (RFC 2181 section 8), and the same section says it specifies a maximum time to live, not a mandatory one. A cache may hold an answer for at most that long, and is free to keep it for less.
Nothing is pushed. When you save a change, the authoritative servers begin returning the new value; secondary servers copy the zone from the primary on the timing in the SOA record. Each recursive resolver keeps the old answer until its own copy expires, and that clock started when that particular resolver last asked. The worst case after a change is one old TTL, and different resolvers switch at different moments inside that window. Other people's resolvers are not yours to flush.
That is also why lowering a TTL at the moment of a change does not help. Caches that fetched the record yesterday are holding it under yesterday's TTL. The order that works is to lower the TTL first, wait one full old TTL, make the change, verify it, then raise the TTL again.
Negative answers are cached too. RFC 2308 has the authoritative server attach the zone's SOA to an NXDOMAIN or empty answer, and the cache keeps that answer for the smaller of the SOA minimum field and the SOA record's own TTL. If a name was queried before you created the record, for instance by a verification step run too early, a resolver can keep answering that the record does not exist for that long. The SOA record holds that number. The DNS lookup tool page does not display it, so read it in your DNS provider's zone editor or with a command-line resolver.
Worth knowing. Changing name servers is different. The delegation records sit in the parent zone (the TLD's servers) and are changed through your registrar, so lowering a TTL inside your own zone does not shorten how long resolvers keep the old delegation.
Reading a DoH answer the way the propagation checker does
The DNS propagation checker sends the same question to four public resolvers over DNS-over-HTTPS, using their JSON interface: Google (dns.google), Cloudflare (cloudflare-dns.com), AdGuard's unfiltered endpoint, and NextDNS. AdGuard's default resolvers filter domains on block lists, which would look like a mismatch for a blocked name, so the tool uses the unfiltered hostname. It supports A, AAAA, CNAME, MX, TXT and NS.
Resolvers differ in cosmetic ways that are not disagreements, so the code normalizes before comparing: trailing root dots are removed, MX hosts are lowercased, quoted TXT chunks are joined, and each answer set is sorted. Records are then grouped by identical content. A resolver that errors or times out is listed separately, because an error is an unknown, not a mismatch. NXDOMAIN counts as a real answer, an empty set.
What it cannot tell you matters as much. It sees four public caches, not your ISP's resolver or the cache inside a browser or operating system. It shows no TTLs and does not query the authoritative servers, so a stale answer cannot distinguish a change that never saved from one that is still expiring. And a disagreement can be deliberate, since geo-steered DNS answers differently by design; the tool's own summary says so.
MX + TXT response, parsed for type MX: Answer data "20 Mail2.Example.com." and "10 mail1.example.com." -> ["10 mail1.example.com","20 mail2.example.com"] same response, parsed for type TXT: Answer data "\"v=spf1 -all\"" -> ["v=spf1 -all"] alias chain www.example.com CNAME edge.example.net, then A 192.0.2.10: parsed for A -> ["192.0.2.10"] parsed for CNAME -> ["edge.example.net"] Status 3 (NXDOMAIN): no error. Status 2: "Resolver failure (SERVFAIL)" Four resolvers, A record for www.example.com: google 192.0.2.10 | cloudflare 192.0.2.10 | adguard 198.51.100.7 | nextdns timed out inSync: false, two groups (google+cloudflare, adguard), failed: nextdns "Resolvers disagree — 2 different A answers are live. Either propagation is still in flight (wait for the old record's TTL to expire), or the domain uses geo-based / load-balanced DNS that intentionally answers differently per resolver."
Three mistakes behind most failed DNS changes
The first is the trailing dot. In zone-file syntax, a name that ends in a dot is absolute and one that does not is relative, so the origin is appended (RFC 1035 section 5.1). An MX target typed as mail.example.com inside the example.com zone becomes mail.example.com.example.com. Dashboards differ in whether they append the origin for you, so the only reliable check is to read the value back from a lookup. The propagation checker accepts one trailing dot on the domain you enter and drops it.
The second is a CNAME where it cannot live: at the apex, or beside MX or TXT records. The third is changing a record without first lowering its TTL, then concluding that the change failed when a cached copy lingers.
Debugging in order: which tool answers which question
Each tool answers a different question, and each has a blind spot.
- DNS lookup shows the A, AAAA, MX, NS and TXT records a domain returns through the server's resolver. It shows values only, with no TTLs. It does not query CNAME, PTR, CAA or SRV, and it strips a leading www. and any scheme before querying, so entering www.example.com looks up example.com. For an alias on a www host, use the propagation checker with type CNAME.
- MX record lookup lists mail hosts by preference, resolves each to addresses, and applies the checks described above.
- DNS propagation checker compares four public resolvers, as described earlier.
- WHOIS lookup asks whois.iana.org over port 43, follows its single referral to the top-level domain's registry server, and extracts registrar, dates, status and name servers. The name servers shown there are the delegation as registered; if they differ from the NS records a lookup returns, a nameserver change has not taken effect or is cached. Registrant fields are often redacted or absent, and the parser depends on each server's output format, so blanks are normal.
- Reverse IP lookup returns the PTR records for an address, plus a list of domains seen on that IP from a third-party service, HackerTarget. That list is a dataset, not something derivable from DNS, and can be incomplete.
Worth knowing. Do not use ANY queries as a shortcut. RFC 8482 allows a responder to answer ANY with a single record set or a synthesized HINFO record, so both DNS tools ask for one type at a time.
Sources
- RFC 1034: Domain Names - Concepts and Facilities (see section 5.3.1, stub resolvers)
- RFC 1035: Domain Names - Implementation and Specification (MX, SOA, master file names)
- RFC 2181: Clarifications to the DNS Specification (section 10 on CNAME, MX and NS; section 8 on TTL)
- RFC 2308: Negative Caching of DNS Queries
- RFC 5321: Simple Mail Transfer Protocol (section 5.1, locating the target host)
- RFC 7505: A "Null MX" No Service Resource Record for Domains That Accept No Mail