How to check txt record of a domain
Run nslookup -type=TXT github.com 8.8.8.8 and read the quoted strings. One record can be printed as several quoted strings on separate lines, and the value is those strings joined with nothing between them. Node's resolveTxt returns each record as an array, which makes the split visible.
Checker offline. Follow the manual steps below, they give the same answer.
Why check this
Run this whenever a third party says "add this record and click verify", and again after any zone edit near it. Domain verification for a search console, a workspace, a payment provider or a certificate authority all live in TXT records, and so do the policies read by every receiving mail server. The failure it prevents is a verification that stays pending for a day because the token was added at example.com rather than at the underscore label the provider named.
The second failure is quieter. A record longer than one DNS string is stored as several strings, and reading only the first one gives a value that is well formed and wrong. Step 2 shows a record where the split falls in the middle of an IPv4 address.
Prerequisites
nslookup, on Windows by default and from bind-utils or dnsutils on Linux.- Node 22 for steps 2 and 3.
dns.Resolverneeds an explicit server here, because the default resolver list is inherited from the operating system. - RFC 1035 section 3.3.14 for the TXT record format, and RFC 7208 section 3.3 for the rule that the strings are joined without a separator.
Steps
- Step 1.
List the TXT records at the name.
nslookup -type=TXT github.com 8.8.8.8Non-authoritative answer: Server: dns.google Address: 8.8.8.8 github.com text = "MS=ms44452932" github.com text = … "openai-domain-verification=dv-3nh33eQwdkotMIAzLrIDVZo1" github.com text = "v=spf1 ip4:192.30.252.0/22 include:spf.protection.outlook.com include:_netblocks.google.com include:_netblocks2.google.com include:mail.zendesk.com include:_spf.salesforce.com include:servers.mcsv.net include:mktomail.com include:sendgrid.net ip4:62.253.2" "27.114 ip4:166.78.69.169 ip4:166.78.69.170 ip4:166.78.71.131 ~all" github.com text =One record, two quoted strings, two lines. Every other record here is one string on one line.
- Step 2.
Read the same records as arrays, so the boundary between strings is explicit. Save it as
txt-chunks.jsand runnode txt-chunks.js github.com.const { Resolver } = require('node:dns'); const name = process.argv[2]; const r = new Resolver(); r.setServers(['8.8.8.8']); r.resolveTxt(name, (err, records) => { if (err) { console.log(err.code); return; } for (const rec of records) { console.log(rec.length + ' string(s), ' + rec.join('').length + ' bytes joined'); rec.forEach((s, i) => console.log(' [' + i + '] ' + s)); } });1 string(s), 55 bytes joined [0] loom-site-verification=f3787154f1154b7880e720a511ea664d 1 string(s), 62 bytes joined [0] anthropic-domain-verification-4az7qn=if8YWuRRqwLycGJDooumzHtxm … 2 string(s), 320 bytes joined [0] v=spf1 ip4:192.30.252.0/22 include:spf.protection.outlook.com include:_netblocks.google.com include:_netblocks2.google.com include:mail.zendesk.com include:_spf.salesforce.com include:servers.mcsv.net include:mktomail.com include:sendgrid.net ip4:62.253.2 [1] 27.114 ip4:166.78.69.169 ip4:166.78.69.170 ip4:166.78.71.131 ~all 1 string(s), 56 bytes joined …String
[0]is 255 bytes and ends inside an address. Joined with nothing, the two giveip4:62.253.227.114. Joined with a space, or read alone, they giveip4:62.253.2, which is a different address and a syntactically valid one. - Step 3.
Query two invented names and a real one, and compare the errors. Save it as
txt-exists.jsand run it with the names as arguments.const { Resolver } = require('node:dns'); const r = new Resolver(); r.setServers(['8.8.8.8']); for (const n of process.argv.slice(2)) { r.resolveTxt(n, (err, recs) => console.log(n.padEnd(30) + (err ? err.code : recs.length + ' record(s)'))); }h2check-probe.mozilla.org ENOTFOUND example.com 2 record(s) h2check-probe.cloudflare.com ENODATABoth invented names are absent, and they came back different. The mozilla.org zone answered that the name does not exist,
ENOTFOUND. The cloudflare.com zone answered with an empty record set,ENODATA, which is also what a real name with no TXT record gets. SoENODATAdoes not prove the name exists. - Step 4.
Run the step 3 script at an aliased name and at its target.
node txt-exists.js www.github.com github.comwww.github.com 24 record(s) github.com 24 record(s)The alias holds no TXT records of its own. The resolver followed the CNAME and answered with the target's 24.
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| One quoted string per line, one record per text = | Ordinary records, each value complete | Compare the value with what the provider issued, character for character |
| Two quoted strings under one text = | One record stored as several strings | Join them with nothing between, then compare |
| ENODATA | No TXT record in the answer. Not proof the name exists: step 3's invented name gave it too | Check the label. A token at _acme-challenge is not at the registrable domain |
| ENOTFOUND | The name itself does not exist | Check the spelling and whether the label was created at all |
| The same records at a name and at its parent | The name is a CNAME and the answer is the parent's | Read How to check cname record. A TXT cannot be added beside a CNAME |
Thresholds
Common mistakes
What to check next
- How to check dns records: the same name, every type, when the token is not where it was expected.
- How to check cname record: the reason a TXT record at a subdomain can be invisible.
- How to check spf record: the record in step 2, read as a sending policy rather than as a string.
- How to check dmarc record: the other policy record, and the one with the strict underscore label.
- How to check a record of domain: the address query that follows the same alias rules.
FAQ
How to check if a txt record exists?
Query the exact name. Records in the answer settle it. ENOTFOUND means the name itself is absent. ENODATA means no TXT record came back, and step 3 shows it for an invented name as well, so it does not prove the label was created.
How to check txt record nslookup?
nslookup -type=TXT <name> <resolver>. Naming the resolver matters when a change is recent, because a cached answer from the resolver your machine defaults to can be older than the zone.
How to verify dns txt record after adding it?
Ask the authoritative nameserver from the SOA answer as well as a public resolver. The authoritative server shows the zone as it stands now, a public resolver shows what clients still hold. A difference between them is the TTL running down, not a mistake.
Why is one record printed as two quoted strings?
Because a single string in a TXT record is capped at 255 bytes, so anything longer is stored as several. The count is a storage detail and the value is the strings joined end to end.
Does the order of TXT records matter?
No. A name can hold many TXT records and the order in an answer is not stable. Each consumer looks for its own prefix, which is why tokens carry one.
Verified
Verified by Maks Vernynslookup Windows 11 build 22631node 22.23.2
Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.
Related on this site
- Checker: dns-records A, AAAA, CNAME, MX, NS, TXT with TTL from two public resolvers, mismatch between resolvers
- DNS migration checklist
- All email and dns checks
basic5 minpublished updated Maks Verny