dig commands every email engineer should know
The dig queries that answer most email questions: SPF, DKIM selectors, DMARC, MX, PTR, MTA-STS and BIMI, plus how to bypass caches when testing.

On this page(14 sections)
- A few flags worth memorizing
- SPF
- DKIM
- DMARC
- MX and null MX
- Reverse DNS (PTR)
- MTA-STS and TLS-RPT
- BIMI
- Reading the status line
- Gotchas that waste time
- Bypassing caches when testing changes
- Ask the authoritative servers directly
- Trace from the root
- Compare several public resolvers
- Reading TTLs
- A one-shot domain check
- Key takeaways
Most email authentication questions can be answered in seconds with dig, if you know which name to ask about and how to read the answer. This is a working cookbook: the queries we reach for most, what a good answer looks like, and the flags that keep caches from misleading you.
A few flags worth memorizing
dig prints a lot by default. These options make it practical:
| Flag | Effect |
|---|---|
+short |
Print only the answer data |
+noall +answer |
Print the answer section with names and TTLs |
@1.1.1.1 |
Ask a specific resolver |
+trace |
Follow delegation from the root, bypassing caches |
-x <ip> |
Reverse lookup of an IP address |
+tcp |
Force TCP, useful for large responses |
+short is ideal for quick checks. +noall +answer is better when you need to see the TTL, which tells you how long a cached answer might persist.
SPF
dig +short TXT example.com | grep -i 'v=spf1'
A good answer is exactly one line starting with "v=spf1". If you see two, the domain has duplicate SPF records and every check returns permerror.
To audit includes, query each one in turn:
dig +short TXT _spf.mailhost.example
Count every include, a, mx, ptr, exists and redirect you encounter across the whole tree. More than ten means permerror. An include that returns nothing at all is a void lookup, and two of those also produce a permerror.
DKIM
You need the selector, which you find in the s= tag of a DKIM-Signature header on a real message.
dig +short TXT k1._domainkey.example.com
A good answer contains v=DKIM1 and a long p= value. A 2048-bit key appears as two or more quoted strings on one line; that is normal. An empty p= means the key has been revoked. No answer means the selector does not exist at that name.
If the provider delegates the selector, you will see the CNAME target first:
dig +noall +answer TXT s1._domainkey.example.com
# s1._domainkey.example.com. 3600 IN CNAME s1.dkim.provider.example.
# s1.dkim.provider.example. 300 IN TXT "v=DKIM1; k=rsa; p=MIIB..."
DMARC
dig +short TXT _dmarc.example.com
Expect one record starting with v=DMARC1. Check the p= value, any sp= or np=, and that rua= uses the mailto: scheme. For a subdomain without its own record, the parent's record governs:
dig +short TXT _dmarc.news.example.com # empty
dig +short TXT _dmarc.example.com # applies to news.example.com
If reports go to another domain, check the authorization record on the receiving side:
dig +short TXT example.com._report._dmarc.reports.example.net
MX and null MX
dig +short MX example.com
A typical answer lists preference values and hostnames. A null MX looks like 0 . and means the domain accepts no mail. No answer at all means senders will fall back to the domain's A or AAAA records.
Then confirm each MX host resolves:
dig +short A mx1.mailhost.example
dig +short AAAA mx1.mailhost.example
Reverse DNS (PTR)
dig +short -x 192.0.2.25
dig +short -x 2001:db8::25
Then check the forward direction for the name you got back:
dig +short A mail1.example.com
Both directions should agree. Receivers treat a missing or mismatched PTR as a negative signal.
MTA-STS and TLS-RPT
dig +short TXT _mta-sts.example.com # "v=STSv1; id=..."
dig +short TXT _smtp._tls.example.com # "v=TLSRPTv1; rua=..."
The MTA-STS policy itself is fetched over HTTPS, so pair dig with curl:
curl -s https://mta-sts.example.com/.well-known/mta-sts.txt
BIMI
dig +short TXT default._bimi.example.com
Expect v=BIMI1 with an l= logo URL and an a= certificate URL (or a= empty).
Reading the status line
+short hides one of the most useful pieces of information: the response status. Run the query without it, or with +noall +comments, and look at the header line that contains status:.
- NOERROR with an answer is the normal case.
- NOERROR with no answer means the name exists but has no record of the type you asked for. A
_dmarcname with a CNAME but no TXT, or a domain with A records but no MX, looks like this. - NXDOMAIN means the name does not exist at all. For DMARC under RFC 9989, a subdomain that returns NXDOMAIN for address and MX lookups is a non-existent domain, which is where the
nptag applies. - SERVFAIL means the resolver could not get a trustworthy answer. On a DNSSEC-signed zone it often points to a signing or delegation problem; try
dig +cd(checking disabled) to see whether the data is there but failing validation.
Receivers react differently to each. An SPF lookup that times out or returns SERVFAIL produces temperror, and the sender usually retries; a missing record produces none. Knowing which one you are dealing with saves a lot of guessing.
Gotchas that waste time
A handful of small things regularly mislead people:
- Search domains. A name typed without a trailing dot may have your local search domain appended by some tools.
digdoes not do this by default, buthostandnslookupcan, depending on configuration. - Multiple strings. Long TXT records come back as several quoted strings separated by spaces. Receivers join them with no separator, so
"v=spf1 include:a.example" " include:b.example ~all"is one record. Do not mistake that output for two records. - Copying from the dashboard. Your DNS provider's interface shows what it intends to serve, which may differ from what it actually serves after a failed save or an import. Always confirm with
digagainst the authoritative server. - Testing from a corporate network. Internal resolvers sometimes serve a split-horizon view with different records. Test against public resolvers too.
Bypassing caches when testing changes
You changed a record five minutes ago and dig still shows the old value. That is usually your local resolver's cache, not a failed change. Options:
Ask the authoritative servers directly
dig +short NS example.com
dig +short TXT example.com @ns1.dnshost.example
The authoritative server always returns the current value. If it shows the new record, the change is published and caches will catch up within the old TTL.
Trace from the root
dig +trace TXT _dmarc.example.com
+trace walks the delegation chain from the root servers, querying each authoritative server directly. It also reveals delegation problems, such as a subdomain delegated to name servers that no longer answer.
Compare several public resolvers
for r in 1.1.1.1 8.8.8.8 9.9.9.9; do echo "$r: $(dig +short TXT _dmarc.example.com @$r)"; done
Different answers across resolvers usually mean a change is still propagating.
Reading TTLs
dig +noall +answer TXT _dmarc.example.com @8.8.8.8
# _dmarc.example.com. 1742 IN TXT "v=DMARC1; p=reject; ..."
From a caching resolver, the TTL counts down: 1742 means that resolver will keep this answer for about 29 more minutes. From the authoritative server, you see the full configured TTL.
A one-shot domain check
Put the common queries together for a quick audit:
d=example.com
echo "MX: $(dig +short MX $d | tr '\n' ' ')"
echo "SPF: $(dig +short TXT $d | grep -i spf1)"
echo "DMARC: $(dig +short TXT _dmarc.$d)"
echo "STS: $(dig +short TXT _mta-sts.$d)"
echo "TLSRPT:$(dig +short TXT _smtp._tls.$d)"
echo "BIMI: $(dig +short TXT default._bimi.$d)"
DKIM is the only record you cannot discover this way, because selector names are arbitrary. Find them in the headers of real messages.
Key takeaways
+shortanswers quick questions;+noall +answershows TTLs and CNAME chains.- Each record lives at a predictable name except DKIM, whose selector comes from message headers.
- Query authoritative servers or use
+traceto see current values without cache interference. - Compare multiple public resolvers to confirm a change has propagated.
Start with Koltrix
Your domain, one inbox, and an API that sends.
A team inbox where AI sorts and drafts (nothing is sent without your click), plus the transactional API and SMTP relay your product sends with. 7 days free, no card.


