In short. A DNS record is a line in your domain's zone shaped as "name — TTL — class — type — value". To add one, open the zone at whichever provider your registrar's NS records point to, pick the type, fill in the name (@ is the root domain, www is a subdomain) and the value. Verify the result with dig, not with a browser: browsers, operating systems and recursive resolvers all cache answers and show you a stale picture.
This is a working guide to operating DNS records: how a zone is put together, how to add and change a record in any control panel, how to handle TXT records (SPF, DKIM, DMARC, ownership verification, ACME), how to verify the result from the command line, and why a change "doesn't apply". A detailed syntax reference for every record type lives in a separate article — DNS record types: A, AAAA, MX, CNAME, TXT and others.

How a DNS zone works and what a resource record is
A DNS zone is the set of records for one domain, served by an authoritative name server. The zone holds the truth about the domain: everything resolvers hand out around the world is a copy of that zone with a limited shelf life.
Which server is authoritative is decided by delegation: the registrar stores NS records for the domain, and those servers are the source of truth. That gives you a rule that saves hours of debugging: edit the zone the domain is actually delegated to. If your registrar points at Cloudflare's name servers and you edit records in an old hosting panel, nothing will change.
The first move in any DNS incident is to find out where the domain is delegated: dig +short NS example.com. The panel you are editing must match what that command returns.
What a resource record is made of
A resource record (RR) is defined in RFC 1035 and has five fields. In a classic zone file they read left to right:
- Name (owner) — the name the record applies to:
example.com.,www.example.com.,_dmarc.example.com.. In web panels this field is usually relative — you typewwwand the zone is appended for you. - TTL — time to live in seconds: how long a caching resolver may keep the answer before asking the authoritative server again.
- Class — always
IN(Internet) in practice; other classes (CH, HS) never appear on the web, and panels usually hide the field. - Type — A, AAAA, CNAME, MX, TXT, NS, SOA, SRV, CAA, PTR and others.
- RDATA — the value itself, whose format depends on the type: an IP address, a domain name, a text string, or a tuple of priority, weight and port.
Records that share a name and a type form an RRset — the unit a resolver fetches and caches as a whole. That has a practical consequence: two A records on the same name cannot have different TTLs, and you cannot update an RRset "partially" — resolvers see it as one indivisible answer.
; a fragment of the example.com zone in BIND format
; addresses come from the ranges reserved for documentation
; (RFC 5737 for IPv4 and RFC 3849 for IPv6)
$ORIGIN example.com.
$TTL 3600
@ 3600 IN SOA ns1.example.net. hostmaster.example.com. (
2026080601 ; Serial
7200 ; Refresh
900 ; Retry
1209600 ; Expire
300 ) ; Negative TTL
@ 86400 IN NS ns1.example.net.
@ 86400 IN NS ns2.example.net.
@ 3600 IN A 192.0.2.10
@ 3600 IN AAAA 2001:db8::10
www 3600 IN CNAME example.com.
api 300 IN A 192.0.2.11
@ 3600 IN MX 10 mx1.example.net.
@ 3600 IN MX 20 mx2.example.net.
@ 3600 IN TXT "v=spf1 include:_spf.example.net -all"
_dmarc 3600 IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"
@ 3600 IN CAA 0 issue "letsencrypt.org"
Note the trailing dot in ns1.example.net. and example.com. — it marks a fully qualified domain name. A name without the dot gets $ORIGIN appended to it. That is the source of one of the most common silent failures, covered below.
DNS record types: summary table
The table below is a working cheat sheet: what each type is for, what goes into the value, and where people trip up. Full syntax, formats and use cases for every type are in the reference article DNS record types.
| Type | Purpose | What the value holds | Common mistake |
|---|---|---|---|
A | Point a hostname at an IPv4 address | IPv4 address | A leftover second A record with the old IP: half the traffic lands on a dead server |
AAAA | Point a hostname at an IPv6 address | IPv6 address | AAAA exists but the server does not listen on IPv6 — some clients just time out |
CNAME | Make a name an alias of another name | A domain name (the canonical one) | CNAME at the apex, or a CNAME sitting next to other records on the same name |
ALIAS / ANAME | Apex-level alias. Not a standard — a provider feature | A domain name the provider expands into A/AAAA | Moving the zone to a provider without the feature: the record simply disappears |
MX | Where inbound mail for the domain is delivered | Priority + mail host name | MX pointing at an IP or at a CNAME; lower number means higher priority, not the reverse |
TXT | Arbitrary text: SPF, DKIM, DMARC, ownership verification, ACME | One or more strings of up to 255 characters | Two SPF records on a domain, a stray space in the value, a truncated DKIM key |
NS | Who is authoritative for the zone | Name server hostnames | NS inside the zone disagree with NS at the registrar — edits go nowhere |
SOA | Zone metadata and replication timers | Primary NS, admin mailbox, serial, timers | Serial not incremented — secondaries never pull the update |
SRV | Service discovery: where a service is and on which port | Priority, weight, port, target host | Missing underscores in names such as _sip._tcp |
CAA | Restrict which CAs may issue certificates for the domain | Flag, tag (issue, issuewild, iodef), value | CAA allows one CA while the certificate is ordered from another — issuance is refused |
PTR | Reverse lookup, IP to hostname (rDNS) | A domain name | People try to set it at the domain registrar; in reality the IP owner controls it |
Individual deep dives: CNAME vs A record, MX records and email setup, TXT records, NS records, SOA records, SRV records, CAA records, wildcard records.
How to add a DNS record: a procedure for any control panel
Interfaces differ between providers, but the sequence is always the same. It also covers editing an existing record — with the difference that you should lower the TTL first (see the caching section).
- Find out where the zone lives.
dig +short NS example.comreturns the real authoritative servers. An empty answer means the domain is not delegated or has been suspended; check its state with WHOIS. - Open the DNS management section at the provider whose name servers that command returned. It may be the registrar, the host, a CDN or a standalone DNS service.
- Pick the record type. A mistake here is quiet but lasting: an A record instead of a CNAME works, yet it stops following the target service when its IP changes.
- Fill in the Name (Host, Subdomain) field. This is the least obvious field — details below.
- Enter the value. An IP for A, a domain name for CNAME/MX/NS, and for TXT the full string including prefixes such as
v=spf1. - Set the TTL. 300 seconds while you are making changes, 3600 or more in steady state.
- Save, then verify against the authoritative server rather than a browser:
dig @ns1.example.net example.com A +short. If the record is there, you did it right — the rest is a matter of time and caches.
What goes in the Name field: blank, @, www and *
Nearly every panel appends the zone name automatically, so the Name field is relative. Values read as follows:
- An empty field or
@— the root domain (apex):example.com. They mean the same thing; panels simply label it differently. www— the subdomainwww.example.com.api.staging— the nested subdomainapi.staging.example.com. Nesting depth is not limited.*— a wildcard: answers for any name that has no records of its own. Details and pitfalls are in the article on wildcard DNS records._dmarc,_acme-challenge,_sip._tcp— service names with a leading underscore. The underscore is mandatory; the panel will not add it for you.
Typingwww.example.cominto a Name field that appends the zone for you produceswww.example.com.example.com. The record saves without complaint and silently does nothing. Always verify withdiginstead of eyeballing the panel.
Why you cannot put a CNAME on the root domain, and what to use instead
The restriction follows from RFC 1034 (section 3.6.2) and its clarification in RFC 2181 (section 10.1): if a name has a CNAME, it must not carry data of any other type. The only exception is for DNSSEC records — RRSIG, NSEC and NSEC3.
The root of a domain, however, is required to carry other types: SOA and NS always live at the apex, and MX and SPF usually join them. So a CNAME on example.com is either rejected by the panel or accepted and quietly breaks mail and delegation.
What to use instead:
- A and AAAA with real addresses — when the target service gives you stable IPs.
- ALIAS / ANAME / CNAME flattening — a provider feature: the DNS server resolves the target name itself and returns an ordinary A/AAAA answer. From the outside it looks like an A record, so nothing in the standard is violated. Each provider names the feature differently, and it does not travel with the zone when you migrate.
- An HTTP redirect from the apex to
www— if the service only supports CNAME. The apex then keeps an A record pointing at whatever issues the 301.
A full comparison and selection criteria are in CNAME vs A record.
The trailing dot and other silent typos
In panels that write a BIND zone file underneath, the value mail.example.com without a trailing dot becomes mail.example.com.example.com. The mistake gives no error on save and surfaces only when mail or a CDN stops working.
The rule is simple: for CNAME, MX, NS and SRV values, enter an FQDN and add the trailing dot if the panel shows one in its other records. If the panel never uses dots, do not add one either. Either way, confirm with dig — it shows the resulting name exactly as the server stores it.

@ mean the root domain, www is a subdomain, * is a wildcard.TXT records: SPF, DKIM, DMARC and domain verification
TXT is the type carrying the most meaning. Formally it is just text, yet email authentication, proof of domain ownership and automated certificate issuance all rest on it. It is also where most mistakes happen, because the content is validated not by DNS but by the consumer — a mail server, a certificate authority or a verification service.
A key property: a single name can hold several TXT records, and that is perfectly normal. The "only one record" rule is not a DNS constraint but a protocol requirement: one SPF policy per domain, one DMARC policy on the _dmarc name.
Domain ownership verification
Search consoles, mail services, payment and advertising platforms confirm domain ownership through a TXT record holding a unique token. It usually goes on the root domain (@), occasionally on a service name.
@ 3600 IN TXT "google-site-verification=aBcD...xyz"
@ 3600 IN TXT "MS=ms12345678"
@ 3600 IN TXT "v=spf1 include:_spf.example.net -all"
Three separate records on the same name is a correct configuration. Do not try to merge tokens into one string: each service looks for its own prefix at the start of a string and will not find it buried inside someone else's text.
Do not delete verification TXT records after a successful check. Many services re-verify ownership in the background and silently revoke access when the record is gone — which you usually discover at the worst possible moment.
SPF: who may send mail on behalf of the domain
SPF (RFC 7208) lists the sources allowed to send mail using your domain in the envelope sender. It lives in a TXT record on the name mail is sent from.
@ 3600 IN TXT "v=spf1 ip4:192.0.2.0/24 include:_spf.example.net mx -all"
The mechanisms:
ip4:/ip6:— specific sender addresses and subnets.include:— pull in a third party's policy (mail provider, newsletter platform, CRM).a,mx— authorise the addresses from the domain's A records or from its MX hosts.-all— hard fail for everything else,~all— soft fail,?all— neutral. Starting with~allis safer; move to-allonce DMARC reports look clean.
Three limits account for most SPF failures:
- One SPF record per name. Two TXT records starting with
v=spf1produce apermerror— the same outcome as having no SPF at all. Multiple providers are combined with multipleinclude:mechanisms inside one string. - No more than 10 DNS lookups while evaluating the policy (RFC 7208, section 4.6.4). Every
include:,a,mx,ptr,existsandredirectconsumes the budget, and nested includes count too. Exceeding it is anotherpermerror. - SPF is not inherited by subdomains. A policy on
example.comsays nothing aboutmail.example.com. Any subdomain that sends mail needs its own record.
DKIM and the selector
DKIM (RFC 6376) publishes the public key a recipient uses to verify a message signature. The key sits in a TXT record at <selector>._domainkey.example.com. The sending system chooses the selector, which is what lets you hold several keys at once and rotate them without downtime.
mail2026._domainkey 3600 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..."
The selector is not decoration: if the mail service signs with selector s1 and the record is published as default._domainkey, verification fails. Take the selector strictly from the sending system's instructions or from the DKIM-Signature header of a message you already sent (the s= tag).
DMARC: what to do with messages that fail
DMARC ties SPF and DKIM to the address in the From header and tells the recipient how to treat messages that fail. The record always lives on the _dmarc name.
_dmarc 3600 IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; pct=100; sp=none; adkim=r; aspf=r"
p=— the policy for the domain:none(observe only),quarantine(spam folder),reject(refuse during SMTP).sp=— a separate policy for subdomains. Without it, subdomains inheritp=.rua=— where aggregate reports go. Moving torejectwithout them is flying blind.pct=— the share of messages the policy applies to; useful for tightening gradually.adkim/aspf— alignment strictness:r(relaxed, allows subdomains) ors(strict).
The working sequence: start at p=none with rua, read reports for a few weeks, move to quarantine, and only then to reject. All three mechanisms are covered in detail in the SPF, DKIM and DMARC guide.
ACME and _acme-challenge: TXT records for certificate issuance
The DNS-01 challenge in the ACME protocol proves control of a domain to a certificate authority through a temporary TXT record:
_acme-challenge 3600 IN TXT "1qA...token..."
_acme-challenge.staging 3600 IN TXT "9zB...token..."
Three practical points:
- Wildcard certificates are only issued via DNS-01. An HTTP challenge cannot prove control over
*.example.comat all. - Parallel issuance needs several TXT records on one name. When a single order covers both
example.comand*.example.com, both tokens are published on_acme-challenge.example.comat the same time. A client that overwrites the previous value instead of adding to it breaks the order. - Delegation via CNAME. If the main DNS provider has no API, point
_acme-challengeonce as a CNAME to a separate zone where automation is allowed. This is the standard trick for manually managed domains.
The 255-character limit and stitching long values together
DNS does not store text as one arbitrary string but as a set of character-strings, each no longer than 255 octets (RFC 1035, section 3.3.14). The total size of a TXT record may exceed that, using several strings in a row.
This is exactly where DKIM breaks: a 2048-bit RSA public key in base64 runs to roughly 390 characters and does not fit into one string. The correct fix is to split the value into quoted chunks; the consumer concatenates them with no separator (specified for DKIM in RFC 6376 section 3.6.2.2 and for SPF in RFC 7208 section 3.3).
; WRONG: a single string longer than 255 characters
; an authoritative server refuses to load the zone,
; and a web panel silently truncates the tail of the key
; RIGHT: several strings inside one record
mail2026._domainkey 3600 IN TXT ( "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1x"
"7Qz3vK9pR2mN4tY6uI8oP0aS1dF3gH5jK7lZ9xC2vB4nM6qW8eR0tY2uI4oP"
"6aS8dF0gH2jK4lZ6xC8vB0nM2qW4eR6tY8uI0oP2aS4dF6gH8jK0lZ2xC4vB"
"IDAQAB" )
The reverse step is reassembling the value when you check it. dig +short prints the strings quoted and space-separated, so you have to join them yourself:
# see how the record is actually split into strings
dig +short TXT mail2026._domainkey.example.com
# reassemble the value with no spaces or quotes
dig +short TXT mail2026._domainkey.example.com \
| tr -d '"' | tr -d ' \n' ; echo
# length of the reassembled key — if it is shorter than expected,
# the value was truncated when it was entered in the panel
dig +short TXT mail2026._domainkey.example.com \
| tr -d '"' | tr -d ' \n' | wc -c
If a DKIM signature fails while the record "looks fine", measure the length of the value first. A key truncated at 255 characters looks entirely plausible in a panel and produces no error anywhere.
Quotes, spaces and escaping
In a zone file a space is a separator. Text inside quotes is one string; text without quotes but with spaces inside falls apart into several strings, and the consumer will join them differently than you expect.
- Always quote TXT values where the panel accepts quotes.
- A stray space breaks an SPF mechanism:
include: _spf.example.netwith a space after the colon is no longer anincludemechanism but noise, and the whole policy becomes invalid. - A quote inside a value is escaped with a backslash:
\". In practice SPF, DKIM and DMARC values need no quotes inside — if they appear, the value was copied together with formatting from documentation. - An unquoted semicolon starts a comment in a zone file and swallows the rest of a DMARC record. Web panels usually handle this; raw BIND configs do not.
How to check DNS records: dig, nslookup and the authoritative server
Checking in a browser is useless: it shows you not the state of DNS but the sum of several caches. Only a query to the authoritative server gives a trustworthy answer.
All the main types in one command
# overview of a domain: every type you normally care about
for t in NS SOA A AAAA MX TXT CAA; do
printf '%-5s ' "$t"
dig +short "$t" example.com | paste -sd' | ' -
echo
done
# the same thing on one line, easy to paste
dig +noall +answer example.com NS SOA A AAAA MX TXT CAA
# service names that never show up in a general listing
dig +short TXT _dmarc.example.com
dig +short TXT default._domainkey.example.com
dig +short SRV _sip._tcp.example.com
+short keeps only the values, which is handy in scripts. For troubleshooting, +noall +answer is more useful: it shows the name, TTL, class and type — everything you need to spot an extra record or an unexpected TTL.
Querying the authoritative server directly, bypassing caches
This is the core diagnostic move. A resolver may keep serving the old value for another day, while the authoritative server always answers with the current state of the zone.
# 1. find the authoritative servers for the zone
dig +short NS example.com
# 2. ask one of them directly
dig @ns1.example.net example.com A +noall +answer
# 3. confirm the answer is authoritative:
# the flags line must contain aa (authoritative answer)
dig @ns1.example.net example.com A | grep -E 'flags|ANSWER SECTION' -A2
# 4. compare all authoritative servers with each other —
# a mismatch means zone replication has not finished
for ns in $(dig +short NS example.com); do
printf '%-24s %s\n' "$ns" "$(dig +short @"$ns" example.com A | paste -sd, -)"
done
# 5. compare the authoritative answer with public resolvers
for r in 8.8.8.8 1.1.1.1 9.9.9.9; do
printf '%-12s %s\n' "$r" "$(dig +short @"$r" example.com A | paste -sd, -)"
done
Read the difference between the two answers like this:
- Authoritative server returns the new value, resolvers return the old one. The change is correct; you are waiting out the TTL. Do nothing.
- Authoritative server returns the old value. The edit was not saved, or it was made in the wrong zone. Go back to "where is the domain delegated".
- Different authoritative servers disagree. The zone is out of sync between primary and secondaries; most often the SOA serial was not incremented.
Tracing delegation with +trace
# walk the chain from the root servers down to the zone
dig +trace example.com
# delegation only, without the data answers
dig +trace +nodnssec example.com NS
# check that delegation in the TLD matches
# the NS records inside the zone itself
dig +short NS example.com # what the zone says
dig @a.gtld-servers.net example.com NS +noall +authority # what the TLD says
+trace answers the question "at which level does it break": if the chain stops at the TLD, delegation at the registrar is wrong; if it reaches the authoritative servers and finds nothing, the problem is inside the zone.
Checking SPF, DKIM and DMARC
# SPF: exactly one string with v=spf1 must be found
dig +short TXT example.com | grep -c 'v=spf1'
# DMARC
dig +short TXT _dmarc.example.com
# DKIM for a specific selector
dig +short TXT selector1._domainkey.example.com
# MX records and the addresses of the mail hosts
dig +short MX example.com
for h in $(dig +short MX example.com | awk '{print $2}'); do
printf '%-28s %s\n' "$h" "$(dig +short A "$h" | paste -sd, -)"
done
# reverse zone: the PTR record for a mail server IP
dig +short -x 192.0.2.25
A DKIM selector cannot be discovered from DNS and cannot be brute-forced — DNS offers no way to list names. Take it from the mail service documentation or from the DKIM-Signature header of a real message.
nslookup on Windows
Windows has no dig out of the box, but it does ship nslookup. The syntax is poorer, yet it is enough to check a record.
nslookup -type=A example.com
nslookup -type=MX example.com
nslookup -type=TXT _dmarc.example.com
REM query a specific server — it is the last argument
nslookup -type=NS example.com 8.8.8.8
nslookup -type=A example.com ns1.example.net
REM interactive mode
nslookup
> set type=TXT
> example.com
> exit
A fuller alternative in PowerShell is Resolve-DnsName example.com -Type MX -Server 8.8.8.8: the output is structured objects, which are much easier to filter.

Why DNS changes are not visible: TTL, caches and negative caching
"I changed the record an hour ago and the site still opens the old one" is the most common DNS complaint. In the overwhelming majority of cases the record is correct and what you are seeing is a cache. Here is where those caches live.
Where the answer is cached
- The recursive resolver — your ISP's or a public one (8.8.8.8, 1.1.1.1) — keeps the answer for exactly TTL seconds from the moment it received it. This is the main layer.
- The operating system cache:
systemd-resolvedon Linux,mDNSResponderon macOS, the DNS Client service on Windows. - The browser cache. Chrome and its derivatives keep their own DNS cache that lives its own life and is not cleared by clearing history.
- The application cache. The JVM caches positive answers aggressively by default, and some HTTP clients and connection pools resolve a hostname once at start-up and never re-check. Hence the classic: a service keeps hitting the old IP after a successful migration until it is restarted.
- Long-lived connections. An established TCP connection will not move to a new IP just because DNS changed.
# Linux (systemd-resolved)
sudo resolvectl flush-caches
resolvectl statistics
# Linux (dnsmasq / nscd, where used)
sudo systemctl restart dnsmasq
sudo systemctl restart nscd
# macOS
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder
# Windows
ipconfig /flushdns
ipconfig /displaydns | findstr example.com
# Chrome: open the internal net-internals page,
# switch to the dns section and press Clear host cache
Negative caching: the record that does not exist yet
The most baffling delay happens when you query a name before creating the record. A negative answer (NXDOMAIN — no such name, or NODATA — the name exists but has no records of that type) is cached too. RFC 2308 defines the mechanism: the lifetime is the smaller of the MINIMUM field in the zone's SOA record and the TTL of that SOA record.
# inspect the zone's negative caching parameters
dig +noall +answer SOA example.com
; output: ns1.example.net. hostmaster.example.com.
; 2026080601 7200 900 1209600 300
; ^^^ negative TTL, seconds
# a negative answer shows up as a status with no ANSWER section
dig new.example.com +noall +comments +authority
The practical takeaway: do not query a name before you create the record. One curious dig against a public resolver, and that resolver will keep answering "no such name" for as many seconds as the negative TTL says. That is why one colleague sees a new subdomain immediately and another has to wait half an hour.
The negative TTL is the only SOA parameter that affects clients directly. The other timers (Refresh, Retry, Expire) only govern synchronisation between primary and secondary servers. If you create subdomains often, keep the negative TTL small — 300 seconds is plenty.
The right sequence for changing a record
- Lower the TTL a day or two before the change to 300 seconds. Lowering the TTL itself propagates at the old, larger TTL — so "in advance" is not a formality here.
- Wait out the old TTL. If it was 86400, that means up to 24 hours; skipping the wait defeats the purpose of lowering it.
- Make the change and immediately verify against the authoritative server:
dig @ns1.example.net example.com A. - Confirm propagation across resolvers — manually with
dig @8.8.8.8or through the DNS propagation check. - Keep the old server alive for at least one full old TTL, ideally a day: some clients and applications hold caches longer than they should.
- Restore the TTL to its working value (3600 or more) once everything is stable.
More on choosing values in the DNS record TTL guide, and on the mechanics of propagation in DNS propagation. Changing the name servers themselves is a separate scenario, covered in how to change your DNS server.
Common DNS record mistakes
Two A records on one name by accident
A migration classic: the new A record is added, the old one is never removed. DNS honestly returns both, clients split roughly evenly between them, and half your users land on a switched-off server. The symptom is deceptive: "the site works every other time", while from your own machine everything looks fine.
# how many A records the name actually has
dig +short A example.com | wc -l
dig +noall +answer A example.com
Multiple A records are a deliberate technique (round-robin), but only while every address is alive. DNS will not notice one of them failing — there is no health checking here.
A CNAME next to other records on the same name
If shop.example.com has a CNAME, it must have no A, no TXT and no MX. Some DNS servers refuse to load such a zone at all; others load it and answer unpredictably, leaving resolvers with contradictory data. It is especially easy to hit by adding a verification TXT record to a name that already points at a CDN via CNAME.
MX pointing at a CNAME or an IP address
An MX value must be a hostname that has an A or AAAA record. RFC 2181 (section 10.3) explicitly forbids an MX target that is an alias, and RFC 5321 requires the same of mail servers. An IP address in an MX field is worse still: some senders will treat it as a hostname and fail to resolve it.
# an MX target must resolve to an address directly, with no CNAME
for h in $(dig +short MX example.com | awk '{print $2}'); do
echo "$h -> $(dig +short "$h" | paste -sd, -)"
dig +short CNAME "$h" | grep . && echo " WARNING: this MX target is a CNAME"
done
The forgotten trailing dot in an FQDN
In BIND-style panels, mx1.example.net without a dot becomes mx1.example.net.example.com. Mail stops arriving while the interface still looks correct. The check is dig +short MX example.com: it prints the full name, including the erroneous tail.
A space in SPF, and two SPF records
Two independent defects with the same outcome — permerror, meaning SPF is treated as absent:
- an extra space after
include:orip4:splits the mechanism into two tokens; - two TXT records with
v=spf1on the same name — typically an old one from a previous provider plus the new one. Merge them into a single string with severalinclude:mechanisms.
Quick check: dig +short TXT example.com | grep -c 'v=spf1' must return exactly 1.
A wildcard covering something other than you expected
Wildcard rules are counter-intuitive:
- an explicit record always beats a wildcard — if
api.example.comhas its own A record, the wildcard never applies to that name; - a wildcard does not answer for a name that has any other record, even of a different type. A TXT record on
test.example.comswitches the wildcard off for A queries to that same name; - a wildcard does not cover names below existing nodes:
*.example.comanswers fora.example.combut not forb.a.example.comonceaexists as a node in the zone; - a wildcard added "just in case" masks typos: a misspelled subdomain resolves instead of honestly returning NXDOMAIN.
DNSSEC breaking after a provider change
The most damaging mistake on this list. If DNSSEC is enabled, the registrar holds a DS record — a fingerprint of the zone's key. Move the zone to another provider and the keys change while the DS at the registrar stays the same. Validating resolvers (which now means most large ones) start returning SERVFAIL, and the domain disappears entirely: website, mail, API. Selectively, too — only for clients whose resolver validates signatures.
# does the registrar publish a DS record
dig +short DS example.com
# does validation pass: the flags line should contain ad (authenticated data)
dig +dnssec example.com A | grep -E '^;; flags'
# explicit chain-of-trust check
delv example.com A
delv +rtrace example.com A
The correct migration order: remove the DS at the registrar, wait out its TTL, move the zone, enable DNSSEC at the new provider, publish the new DS. The signing mechanics are covered in DNSSEC explained.
Editing the wrong zone
The domain is delegated to a CDN while records are edited in the hosting panel; or the domain has two zones at two providers, left over from an old migration. Externally this looks like "DNS has not updated for a week". One command settles it: dig +short NS example.com — then edit where the zone actually lives.
Diagnose by symptom: which record to look at
| Symptom | Record to inspect | Command | Usual cause |
|---|---|---|---|
| Inbound mail does not arrive | MX and the A records of the hosts it points to |
dig +short MX example.com |
MX missing, pointing at a CNAME, or at a name with no A record; a forgotten trailing dot |
| Outbound mail lands in spam | TXT (SPF, DKIM, DMARC) and the PTR of the sending IP |
dig +short TXT example.com, dig +short -x 192.0.2.25 |
No SPF or two of them; wrong DKIM selector; PTR not set by the IP owner |
| The site still serves the old version | A, AAAA and their TTLs |
dig @ns1.example.net example.com A versus dig @8.8.8.8 example.com A |
TTL has not expired; a stale A record remains; the edit was made in the wrong zone |
| A certificate will not issue | CAA and the TXT at _acme-challenge |
dig +short CAA example.com, dig +short TXT _acme-challenge.example.com |
CAA authorises a different CA; the token is missing or was overwritten by a second order; validation ran before propagation |
| The domain does not resolve at all | NS, TLD delegation, DS |
dig +trace example.com, dig +short DS example.com |
Domain expired or removed from delegation; DS does not match the zone keys — SERVFAIL |
| A subdomain fails while the root works | The subdomain's A/CNAME and any wildcard |
dig +noall +answer sub.example.com ANY |
The name was entered as an FQDN in a relative field; the wildcard is disabled by another record type |
The root fails while www works |
A/AAAA at the apex |
dig +short A example.com |
A CNAME was placed at the apex and dropped; an ALIAS record did not survive the zone migration |
| Service verification keeps failing | TXT on the required name |
dig +short TXT example.com |
The token was merged with another value; the record was created on www instead of the root; a negative answer was cached before the record existed |
| Works for some people, not for others | Divergence between authoritative servers, DNSSEC | compare dig @ns across every NS of the zone |
Zone out of sync (serial not incremented); validating resolvers discard the answer |

Bulk operations: zone export, import and zero-downtime migration
Once a zone has dozens of records, retyping them in a new panel is guaranteed to lose something — usually a verification TXT record or an obscure subdomain nobody remembers.
Exporting and importing a zone
- BIND-format export — available at most DNS providers. It is a plain zone file, and the same format is accepted by the new provider's import.
- The provider API — more reliable than manual export when you have many zones: you can dump and load records with a script and keep the zone in version control.
- AXFR (zone transfer) — technically the most complete method, but public providers almost always disable it. If yours allows AXFR from trusted addresses, the dump is a single command.
# dump the zone over AXFR (only works if it is permitted)
dig @ns1.example.net example.com AXFR > example.com.zone
# if AXFR is closed, build a snapshot from the names you know
for name in @ www api mail staging _dmarc _acme-challenge; do
n=$( [ "$name" = "@" ] && echo example.com || echo "$name.example.com" )
dig +noall +answer "$n" A AAAA CNAME MX TXT SRV CAA
done > zone-snapshot.txt
# normalise and compare two snapshots (before and after the move)
sort zone-before.txt > a.txt
sort zone-after.txt > b.txt
diff -u a.txt b.txt
Migrating a zone without downtime
- Export the zone from the current provider and save a
digsnapshot of every name you know about — that is your reference for comparison. - Create the zone at the new provider and load the records. Leave the NS records at the registrar alone for now.
- Compare the zones directly: query the old and the new authoritative servers with identical requests and diff the output line by line. Find discrepancies before the cutover, not after.
- Check DNSSEC. If it is enabled, remove the DS at the registrar first and wait out its TTL. Skipping this step takes the whole domain down.
- Switch the NS records at the registrar. The delegation TTL in the TLD zone is usually measured in days, and you cannot lower it — it is not yours.
- Keep the old zone alive for at least a week: some resolvers will keep hitting the previous servers until the delegation cache expires.
The migration rule: the old zone is switched off last. While both sets of name servers answer identically, the cutover is invisible to users; the moment they start to disagree, you get intermittent failures that are nearly impossible to reproduce.
Monitoring DNS records and alerting on changes
DNS is the single point whose modification instantly redirects all traffic for a domain. Meanwhile several people usually hold write access, and not every provider keeps an audit log. At a minimum, watch:
- NS and DS — a change here means a change of control over the domain. An unexpected edit warrants immediate investigation.
- A and AAAA on primary names — a substituted address redirects traffic and enables someone to obtain a certificate for your domain.
- MX — a hijacked mail route is the quietest and the most damaging change of all.
- TXT holding SPF, DKIM and DMARC — removing or weakening them opens the domain to spoofed mail.
- CAA — removing the CA restriction usually precedes an unauthorised certificate issuance.
- Domain expiry — an expired domain drops out entirely, no matter how carefully the records were configured.
The simplest approach is to take a reference snapshot regularly and compare it with the current state, notifying on any difference:
#!/bin/sh
# /usr/local/bin/dns-diff.sh — compare a zone snapshot with the reference
DOMAIN="example.com"
BASE="/var/lib/dns-watch/$DOMAIN.base"
NOW="/tmp/$DOMAIN.now"
{
dig +short NS "$DOMAIN" | sort
dig +short DS "$DOMAIN" | sort
dig +short A "$DOMAIN" | sort
dig +short AAAA "$DOMAIN" | sort
dig +short MX "$DOMAIN" | sort
dig +short TXT "$DOMAIN" | sort
dig +short CAA "$DOMAIN" | sort
} > "$NOW"
if [ -f "$BASE" ] && ! diff -q "$BASE" "$NOW" >/dev/null; then
diff -u "$BASE" "$NOW" | mail -s "DNS changed: $DOMAIN" ops@example.com
fi
cp "$NOW" "$BASE"
Running it every 15 minutes is enough — a scheduler line such as */15 * * * * under a user allowed to send notifications. If you would rather not run your own infrastructure, website and domain monitoring checks availability and key domain parameters on a schedule and alerts you when something changes.
How to check your DNS records with Enterno.io
- DNS Lookup — every record type for a domain in one report, with a choice of resolver and a history of checks.
- Single DNS record check — a targeted query for one name and one type, when you only need to confirm a TXT or a CNAME.
- DNS propagation check — how the record looks from resolvers in different regions; the answer to "has it propagated yet".
- MX Lookup — the domain's mail routes and the addresses of the hosts they point to.
- Email configuration check — SPF, DKIM and DMARC together, with an explanation of why messages end up in spam.
- WHOIS — registrar, expiry date and the domain's current name servers.
- Monitoring — scheduled checks of the domain with alerts when key parameters change.
FAQ
How long should I wait after changing a DNS record?
As long as the record's TTL was before the change — that is the upper bound. If the TTL was 3600, every resolver that queries your zone will see the new value within an hour at most. "48 hours" is a myth inherited from name server changes, where the delegation TTL really is measured in days. Check the actual state with the propagation check.
Why does dig show the record while the browser still opens the old site?
Between dig and the browser sit three more caches: the operating system, the browser itself, and connections that are already open. Flush the OS cache, close the tab, try a private window. If only some users are affected, you probably forgot to delete the old A record.
Can I have two A records with different IPs?
Yes — that is round-robin, and resolvers will hand out the addresses in turn. But DNS performs no health checks: if one address stops responding, a share of requests will keep going to it. Round-robin is fine for spreading load across servers you know are alive; it is not a substitute for a load balancer with health checking.
Why does DKIM fail when the record is clearly there?
Two causes cover almost every case. First, the value was truncated at 255 characters: a long key must be entered as several quoted strings. Second, the selector is wrong — the record name must match exactly what your mail service signs with. Measure the reassembled value with dig +short TXT selector._domainkey.example.com | tr -d '" ' | wc -c.
What is negative caching, and why does a new subdomain "not appear"?
If a resolver has already asked for that name and got "no such name", it remembers the negative answer for the period set in the zone's SOA record (typically 300–3600 seconds). So do not check a subdomain before you create it: your own check is what creates the delay you will then complain about.
Who configures the PTR record?
The owner of the IP address — your hosting or internet provider, not the domain owner. The reverse in-addr.arpa zone is delegated together with the address block. Most providers expose PTR in the server control panel; the rest handle it by support request. For a mail server a PTR record is mandatory: without it, messages are almost guaranteed to be treated as spam.
What should I verify after moving hosting?
In order: the NS at the registrar match the zone you are editing; A and AAAA point to the new server with no leftovers; MX records survived the move; TXT records for SPF, DKIM, DMARC and verification tokens were carried over in full; CAA does not block certificate issuance; the DS at the registrar matches the new zone's keys.
DNS records checklist
- Confirmed where the domain is delegated with
dig +short NS example.com— and I am editing that zone. - I know what the Name field means in my panel: blank and
@are the root,wwwis a subdomain, no FQDN needed. - The apex carries A/AAAA or ALIAS, never a CNAME.
- No name with a CNAME carries any other record.
- MX records point at hostnames with A records, not at CNAMEs or IPs; no trailing dot is missing from the FQDN.
- Exactly one TXT record with
v=spf1, no stray spaces, within the 10-lookup budget. - DKIM is published under the correct selector and is not truncated at 255 characters.
- DMARC exists on
_dmarc, starting atp=nonewith anruaaddress for reports. - Verification TXT records were not deleted after the check passed.
- TTL was lowered ahead of the change, not at the moment of it.
- The result was verified against the authoritative server with
dig @ns, not in a browser. - Old A records and leftovers from the previous host have been removed.
- With DNSSEC enabled, the migration order was respected: remove the DS first, then change the zone.
- A reference zone snapshot exists and is compared regularly with alerting on changes.
- Domain expiry is monitored — an expired domain overrides every other setting.