Ask DNS if it is DNS
Asks the site itself, over DNS, what it can answer.
dig -4 @1.1.1.1 help.isitdns.net TXTGO NOERROR and the help text in TXT strings
NO GO NXDOMAIN, which means you are not reaching the isitdns.net zone
Captured 2026-10-06 at 21:24:21Z, at
surface=8: 1.1.1.1 over IPv4, no port-53 interception on the path.
The names#
| Name | What comes back |
|---|---|
help.isitdns.net | TXT: the verdict line, the menu, the receipt, the token line. A: the word as a ladder code |
isit.isitdns.net | TXT: the token line, then the word: no, mostly, maybe or no data. A: the word as a ladder code |
why.isitdns.net | TXT: the resolvers that are not ok, or all measured resolvers answering |
ksk.isitdns.net | TXT: the root KSKs published, the roll date, and the public resolvers by sentinel result |
cloudflare.isitdns.net, google.isitdns.net, quad9.isitdns.net, adguard.isitdns.net | TXT: that resolver's status line, then one string per measured leg |
_isitdns.isitdns.net | TXT: the version record |
Every record has a TTL of 60 seconds, except the version record, which has 3600.
The version record#
dig +short _isitdns.isitdns.net TXT"v=isit1 surface=8 round=180 spec=https://isitdns.net/wiki/tools/ask-dns"v=isit1 is the format of this record. surface=8 is the version of the records on this page. round is how often the zone changes, in seconds. spec is this page.
The tokens, for a script#
isit and help each carry one token line of key=value pairs.
| Token | Value |
|---|---|
state | ok for no, degraded for mostly, failing for maybe, stale for no data |
bad | resolvers failing from every network that read them; state=degraded with bad=0 means one or more failed from some networks and answered from others |
measured | resolvers with a verdict |
probes | the number of probes the verdict was read from this round; 0 when there is no reading |
seats | the same value as probes, kept for parsers of the old key, removed at surface=9 |
asof | the stamp of the verdict line, RFC 3339 with Z |
The menu in help lists probes, not seats (legacy key).
The A record ladder#
help and isit answer an A query with a loopback code, the DNSxL convention of RFC 5782 section 2.1.
| Address | Word |
|---|---|
127.0.0.2 | no |
127.0.0.3 | mostly |
127.0.0.4 | maybe |
127.0.0.5 | no data |
127.0.0.1 is never a code (RFC 5782 section 5). A 127.0.0.1 answer for these names did not come from isitdns. Any other address is not a word.
dig +short isit.isitdns.net AThe grammar#
ABNF, RFC 5234. Quoted strings are case-sensitive, the %s form of RFC 7405.
- A TXT record is one or more character-strings of at most 255 octets (RFC 1035 section 3.3.14). Concatenate the strings of a record with nothing between them (RFC 7208 section 3.3).
helpis four parts,help-1tohelp-4, each found by its prefix:is it DNS?,ask:,receipt:,state=. A part spans one or more strings.isitis exactly two strings:isit-1, thenisit-2.- A resolver record matches
resolveronce its strings are concatenated. Each leg is one string beginningv4orv6; every string but the last ends in SP. - These records end in
as of <time>Z:why,ksk. A resolver record ends in a leg string when it has one. - The version record is one string matching
version-record(rule 1). The token line matchestoken-line(rule 8).
stamp = 4DIGIT "-" 2DIGIT "-" 2DIGIT "T" 2DIGIT ":" 2DIGIT ":" 2DIGIT "Z"
day = 4DIGIT "-" 2DIGIT "-" 2DIGIT
as-of = "as of " stamp
count = "0" / %x31-39 *DIGIT
word = "no" / "mostly" / "maybe" / "no data"
help-1 = "is it DNS? " verdict " isitdns monitors common public DNS providers. " [ count " no data, " ] as-of
verdict = "no."
/ "mostly. " count " of " count " resolvers are struggling."
/ "maybe. " count " of " count " resolvers are down."
/ "no data. " nd-reason "."
/ "no data yet. " yet-reason "."
nd-reason = "fewer than two networks have reported; the last reading is " age " old"
/ "the probe readings could not be read"
/ "the board round's reading is stamped in the future; no verdict until the clocks agree"
/ "no database is bound; nothing was read"
yet-reason = "no probe has reported"
reason = nd-reason / yet-reason
age = count "m" / count "h " count "m" / count "d"
help-2 = "ask: isit why ksk cloudflare google quad9 adguard, all .isitdns.net TXT; A ladder 127.0.0.2 no, .3 mostly, .4 maybe, .5 no data; 127.0.0.1 is never ours; tokens, one string in isit and here: state (ok, degraded, failing, stale), bad, measured, probes, asof; concatenate the strings of a line, no separator"
help-3 = "receipt: https://isitdns.net; probe: ours not yours, your path: https://isitdns.net/wiki/troubleshooting/test-your-own-path"
help-4 = tokens
tokens = "state=" state " bad=" count " measured=" count " probes=" count " seats=" count " asof=" stamp
state = "ok" / "degraded" / "failing" / "stale"
version = "v=isit1 surface=" count " round=" count " spec=" spec-url
spec-url = "https://isitdns.net/" 1*( %x61-7A / DIGIT / "-" / "." / "_" / "~" / "/" )
version-record = "v=isit1" *( SP vtag )
vtag = vkey "=" vval
vkey = 1*( %x61-7A / DIGIT )
vval = 1*%x21-7E
token-line = token *( SP token )
token = tkey "=" tval
tkey = 1*%x61-7A
tval = 1*%x21-7E
isit = isit-1 isit-2
isit-1 = tokens
isit-2 = word
ladder-code = "127.0.0.2" / "127.0.0.3" / "127.0.0.4" / "127.0.0.5"
why = not-fine ", " as-of
/ "all measured resolvers answering, " as-of
/ "no data: " reason ", " as-of
not-fine = rstate *( ", " rstate )
rstate = board-id " " ( "slow" / "partial" / "bad" )
board-id = ldh-label
ldh-label = alnum [ *38( alnum / "-" ) alnum ]
alnum = %x61-7A / DIGIT
resolver = status *( SP leg )
resolver-id = "cloudflare" / "google" / "quad9" / "adguard"
status = resolver-id " " rstatus " " latency [ ", " note ] ", " dnssec ", " as-of
/ resolver-id " no data, " as-of
rstatus = "ok" / "slow" / "partial" / "bad"
note = "failing from " dlist "; slow from " dlist / "failing from " dlist / "slow from " dlist
dlist = dname *( ", " dname )
latency = count " ms" [ " from " dname ] / "unreachable" / "n/a"
dname = probe-name / domain-id
probe-name = "the" 1*%x61-7A
domain-id = 1*%x61-7A
dnssec = "dnssec validating" / "dnssec not validating" / "dnssec unknown"
leg = family " " transport " " leg-latency " " leg-state
family = "v4" / "v6"
transport = "doh" / "dot" / "tcp53" / "udp53"
leg-latency = count "ms" / "--"
leg-state = "ok" [ SP rcode ]
/ ( "unreachable" / "failing" ) [ " from " probe-list ]
probe-list = probe-name [ *( ", " probe-name ) " and " probe-name ]
rcode = 1*%x41-5A
ksk = keys "; roll " day "; of " count " public resolvers via sentinel: " count " trust " tag ", "
count " do not yet, " count " no sentinel support, " count " not validating, "
count " no determination (both SERVFAIL), " count " unreadable; " as-of
keys = "root KSKs published: " tag *( ", " tag ) / "root KSKs: unread"
tag = 1*5DIGITReading it as a client#
- The version record is one TXT record at
_isitdns.isitdns.netthat matchesversion-record. - A record whose first tag is not
v=isit1is not an isitdns version record, and a client MUST stop reading it. - A client MUST ignore a version tag it does not know.
- More than one version record, no version record, a version key twice, or a
surfacethat is absent or not acountof at most 9 digits means the version is unknown;roundandspecdo not change it. - A client that reads a
surfaceit does not implement, or an unknown version, MUST NOT make a verdict from these records and MUST NOT report a DNS outage. - Such a client MAY show the raw records and the
surfacenumber. surfaceversions these DNS records only; the MCP server is versioned by its ownserverInfo.versionand itstools/list.- The token line matches
token-line: keys separated by single spaces, each key once, order not significant. - A client MUST ignore a token key it does not know.
stateandasofare required; a token line without both, or with a key twice, is not usable.- An absent key is not stated; it never means 0.
- A stamp 600 s old or older, or more than 180 s ahead, is not live: live is -180 <= age < 600, where age is the client's UTC clock minus the stamp, in seconds. A stamp that is not a valid RFC 3339 date-time has no age and is not live; a client that knows its clock is not set MUST NOT make a verdict.
- A stamp that is not live is
no data, whatever the word beside it says. - The freshness rule covers
help,isit,whyand the resolver records, andkskwith 1200 s in place of 600 s. - When the TXT stamp is live, the TXT record wins over the A record.
- An A answer without a live TXT stamp beside it is
no data. isitdns.netis DNSSEC-signed. A client SHOULD validate the answer itself, or accept AD=1 only from a validating resolver it trusts over a secure channel (RFC 4035 section 4.9.3); it SHOULD label any other answer unauthenticated, a label that does not change the verdict.- A bogus answer (SERVFAIL, or extended DNS error 6 or 9 of RFC 8914) MUST NOT be read as a verdict.
- NOERROR with a record is the answer.
- NOERROR with no record, or NXDOMAIN, for a name not on this page means it is not on this surface. For a name on this page, NOERROR with no record is no reading, and NXDOMAIN was made on the path, except
ksk, absent while its board is refused: NXDOMAIN or NOERROR with no record forkskis no reading. None of these is a verdict. - SERVFAIL, REFUSED, a timeout, or TC=1 without a retry over TCP is no reading, never a verdict.
- Each name carries one TXT record, and
helpandisitone A record; more than one is no reading for that name. - A
surfacechange passes through an unknown version: the version record dropssurfaceat least 3600 s before the records change, and the newsurfaceappears at least 60 s after them. - A resolver record carries each
familyandtransportpair at most once, order not significant; with a pair twice, its legs are not usable. - A
helporisitTXT whose word (no data yetisno data) andstatedisagree, or whoseas ofandasofdiffer, is no reading.
This reader applies rules 10, 12, 13 and 25 to isit:
dig +short isit.isitdns.net TXT | python3 -c '
import calendar, math, re, sys, time
a = sys.stdin.read()
p = re.findall(r"(\w+)=([^\s\"]+)", a)
t = dict(p)
for k in ("state", "asof"):
if k not in t: sys.exit("not fine: no %s token in the answer" % k)
if len(t) != len(p): sys.exit("not fine: a token key appears twice")
w = {"no": "ok", "mostly": "degraded", "maybe": "failing", "no data": "stale"}
if w.get((re.findall(r"\"([^\"]*)\"", a) or [""])[-1]) != t["state"]: sys.exit("not fine: word and state disagree")
if not re.fullmatch(r"\d{4}-\d\d-\d\d[Tt]([01]\d|2[0-3]):[0-5]\d:([0-5]\d|60)[Zz]", t["asof"]): sys.exit("not fine: asof is not a valid stamp")
try: age = math.floor(time.time() - calendar.timegm(time.strptime(t["asof"].upper(), "%Y-%m-%dT%H:%M:%SZ")))
except ValueError: sys.exit("not fine: asof is not a valid stamp")
ok = t["state"] == "ok" and -180 <= age < 600
print("fine, %ds old" % age if ok else "not fine: state=%s, %ds old" % (t["state"], age))'See also#
- dig-tool runs the same queries from the browser
- dig-flags explains
+shortand the rest - when-its-dns is what to do when the word is not
no - txt is the record type doing the work here