is it dns? / wiki ← back to the live monitor

Worked examples

The deep-dive companion to the "Learning examples" chips on the front-page dig tool at isitdns.net. Each chip is a button you click to fire a real DNS query from the isitdns.net edge worker and see the answer instantly, with annotated output by default. No install, no terminal, no resolver-path noise. This page is where you land when you click Learn more β†— on any chip. One section per chip, explaining what just happened, why it matters, and where to go deeper.

πŸ’» Prefer your own terminal? The dig-along is the same hands-on spirit, but you copy-paste each command into your shell. Same DNS, different vantage.

Each section below is anchored to a slug that matches the chip's preset name on the SPA: https://isitdns.net/#dig?preset=<slug>. If a chip ever produces output that doesn't match what's described here, the live behavior is the source of truth: open a note for the doc team and someone will reconcile.

Quick jump#

ConceptSlug
DNSSEC validateddnssec-validated
NXDOMAINnxdomain
ECS: US vs Asia-Pacificecs-east-vs-west
DNSSEC: valid vs forceddnssec-valid-vs-cd
PTR and reverse DNSptr-reverse-dns
Root nameserversroot-nameservers
DoH vs DoTdoh-vs-dot
Family filter: blocked vs allowedfamily-filter
Ad blocking: AdGuard vs Googlead-blocking-adguard
Ad blocking: Control D vs Googlead-blocking-controld
ANY and RFC 8482any-rfc-8482
Compare all (MX)compare-all-mx

DNSSEC validated#

▢️ Run live: isitdns.net/#dig?preset=dnssec-validated. Click to load and auto-run this chip on the front-page tool.

dig iana.org A @1.1.1.1 +dnssec
nslookup -type=A iana.org 1.1.1.1

nslookup can't show this here, use dig. nslookup returns the A record but does not display the AD flag, RRSIG records, or any DNSSEC-related fields.

What you'll see

  • Status: NOERROR
  • One or more A records for iana.org in the answer section
  • The ad flag in the flags line (;; flags: qr rd ra ad;) IF your network surfaces it; some intermediate local resolvers strip the AD flag before it reaches a stub like dig. The more reliable signal is the presence of RRSIG records alongside the A records.
  • Often a TTL counting down (Cloudflare's 1.1.1.1 caches signed answers like any other recursive)

Live proof: AD bit via DoH

curl -s -H 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=iana.org&type=A'
{
  "Status": 0,
  "AD": true,
  "Question": [{"name": "iana.org", "type": 1}],
  "Answer": [{"name": "iana.org", "type": 1, "TTL": 1658, "data": "192.0.43.8"}]
}

"AD": true confirms Cloudflare's validating resolver set the Authenticated Data bit. The JSON DoH response preserves the AD flag even when local network tools strip it. (JSON responses on this page are pretty-printed and trimmed to the interesting fields; TTLs are cache snapshots that count down, so expect different values when you run it.)

Why this matters

DNSSEC adds cryptographic signatures to DNS records. When +dnssec is set, the resolver fetches not just the A record but also the RRSIG (signature) covering it, the DNSKEY records that signed it, and the DS records linking the zone to its parent.

       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚   root   β”‚  ← trust anchor (in every validator)
       β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
             β”‚ publishes DS for "org"
             β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚   .org   β”‚
       β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
             β”‚ publishes DS for "iana"
             β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚ iana.org β”‚  ← RRSIG covers the A records
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The recursive walks this chain top-to-bottom. If every link cryptographically verifies, it sets the AD (Authenticated Data) bit in the response header. The AD bit is the resolver's way of telling the client "the data you're about to use came back signed and the signatures checked out."

iana.org is a fitting demo because IANA administers the root zone. If their own zone weren't signed, nothing else's would matter.

Important caveat

The AD flag is hop-by-hop, not end-to-end. A laptop's stub resolver trusts whatever the upstream recursive says. If the path between the stub and the recursive doesn't preserve the AD bit (some routers, some captive portals, some corporate proxies strip it), you'll see qr rd ra instead of qr rd ra ad even when the zone is properly signed. The presence of RRSIG records in the response is a more reliable indicator.

Read deeper


NXDOMAIN#

▢️ Run live: isitdns.net/#dig?preset=nxdomain. Click to load and auto-run this chip on the front-page tool.

dig this-domain-truly-does-not-exist-12345.example A @1.1.1.1
nslookup -type=A this-domain-truly-does-not-exist-12345.example 1.1.1.1

What you'll see

  • Status: NXDOMAIN
  • Empty answer section
  • An SOA record in the authority section: the negative cache record telling downstream resolvers how long the "no" is good for

Live proof

curl -s -H 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=this-domain-truly-does-not-exist-12345.example&type=A'
{
  "Status": 3,
  "AD": true,
  "Question": [{"name": "this-domain-truly-does-not-exist-12345.example", "type": 1}],
  "Authority": [{"name": "", "type": 6, "TTL": 86400,
    "data": "a.root-servers.net. nstld.verisign-grs.com. 2026071700 1800 900 604800 86400"}]
}

"Status": 3 is NXDOMAIN. The Authority section carries the root SOA; .example has no delegation at all, so the root itself is the closest ancestor that can say "no." (The SOA serial is a datestamp the root operators bump with every edit; it and the TTL will read differently when you run it.)

Why this matters

NXDOMAIN is one of the cleanest DNS responses. It means: "I am authoritative for this part of the name tree, and the name you asked for does not exist." Not "I don't know," not "ask someone else", a definitive no.

client                 recursive            authoritative for ".example"
  β”‚                       β”‚                          β”‚
  β”‚ A nonexistent.example β”‚                          β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Άβ”‚                          β”‚
  β”‚                       β”‚  A nonexistent.example   β”‚
  β”‚                       β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Άβ”‚
  β”‚                       β”‚  ◀── NXDOMAIN + SOA ─────│
  β”‚  ◀─ NXDOMAIN + SOA ────                          β”‚
  β”‚                       β”‚  (cache the "no"         β”‚
  β”‚                       β”‚   for SOA.minimum sec)   β”‚

The SOA record in the response carries a minimum field (RFC 2308). That value, capped by the SOA's own TTL, tells downstream resolvers and stub clients how long they may cache the negative answer. Without negative caching, every typo, every misconfigured DNS hostname, every dead third-party tracker would re-query the authoritative every single time. Negative caching is one of the silent reasons DNS scales.

NXDOMAIN vs NOERROR (zero answers) vs SERVFAIL

These three are commonly confused:

ResponseMeaning
NXDOMAIN"This name does not exist anywhere in the namespace."
NOERROR with empty answer section"This name exists, but has no record of the type you asked for." (Asking for AAAA on an IPv4-only name, for example.)
SERVFAIL"Something broke. I can't even tell you whether this name exists or not." Could be DNSSEC failure, broken upstream, resolver bug.

The example uses .example because RFC 2606 reserves it: that TLD is guaranteed to never exist in the public DNS, so the test is deterministic.

Read deeper

  • DNS fundamentals: TTL behavior including negative caching
  • Anatomy of a DNS query: how an NXDOMAIN gets assembled
  • RFC 8020: NXDOMAIN really means NXDOMAIN (resolvers shouldn't keep asking child names of an NXDOMAIN parent)
  • RFC 2308: negative caching
  • RFC 2606: reserved domain names (updated by RFC 6761, "Special-Use Domain Names")

<a id="ecs-east-vs-west"></a>

ECS: US vs Asia-Pacific#

▢️ Run live: isitdns.net/#dig?preset=ecs-east-vs-west. Click to load and auto-run this chip on the front-page tool.

dig www.adobe.com A @8.8.8.8 +subnet=4.2.2.0/24
dig www.adobe.com A @8.8.8.8 +subnet=210.53.0.0/16

nslookup can't show this here, use dig. nslookup has no equivalent of +subnet (the ECS hint) and cannot send EDNS Client Subnet options.

πŸ’¬ Why the rename. The previous chip ran against www.microsoft.com with two US-only subnet hints. Microsoft has since moved part of its CDN onto its own network, and Akamai stopped varying responses across US-only subnets. The example produced the same IP both ways and stopped demonstrating anything. Switched to www.adobe.com (still on Akamai's geo-DNS) and widened the gap from US-East-vs-US-West to US-vs-APNIC. The anchor #ecs-east-vs-west is intentionally unchanged so existing chip links keep working. See the live-status note below: whether the spoofed hint changes the answer is itself a moving target.

What you'll see

Both queries succeed (NOERROR). When a CDN is actively geo-routing, the answer sections return different edge IPs: the CDN picks the server closest to the subnet hint the resolver passed along. You may see different IPs for the two subnet hints, or you may see the same IP. CDN operators consolidate edge infrastructure over time, and a domain that varied by region last year may stop varying today. Run both commands and compare: results vary by domain and by when you ask. The live SPA chip shows current behavior from the isitdns.net edge vantage.

  • US (4.2.2.0/24) β†’ an Akamai US edge (when the CDN keys the answer on the hint)
  • Asia-Pacific (210.53.0.0/16) β†’ an Akamai Asia edge (when the CDN keys the answer on the hint)

Live status (verified 2026-07-17): Akamai currently answers www.adobe.com with an ECS scope of /0, meaning "this answer is not subnet-specific", so both hints return the same edge IPs from a given vantage. The name is still geo-routed, but the mapping follows where the query enters the CDN's authoritative, not the spoofed hint: at the same moment, a Tokyo probe through 8.8.8.8 got 23.205.46.x while a Los Angeles probe got 23.217.118.x. You can read the CDN's decision right off dig's OPT pseudosection: CLIENT-SUBNET: 210.53.0.0/16/0 is sent-prefix / scope, and a scope of 0 says the hint did not shape the answer (RFC 7871 Β§6 defines SCOPE PREFIX-LENGTH).

Why this matters

EDNS Client Subnet (ECS, RFC 7871) lets a recursive resolver attach a hint to its upstream query: "the actual end user is in subnet X." A geo-aware CDN can then pick a server close to the user, not close to the resolver.

[ client 210.53.0.5 (APNIC region) ]
    β”‚
    β”‚ A www.adobe.com
    β–Ό
[ 8.8.8.8 recursive ]  ECS=210.53.0.0/16 ──▢ [ Akamai authoritative ]
                                                 picks the nearest edge
                                                 for the APNIC subnet
                                                 (Asia edge, not US-East)
                       ◀── A 23.205.46.x (an Asia edge) ──

The +subnet= flag on dig lets you spoof the ECS hint from your local tool. You're not actually on that network: you're just telling the resolver "pretend the client is here." This is how the SPA's two side-by-side queries work: same name, different ECS hints, and, whenever the CDN keys the answer on the hint, different continents and different answers. When the CDN returns scope /0 (the current Akamai behavior for this name), the two answers match, and the scope field is the tell.

The privacy story

Cloudflare's 1.1.1.1 deliberately does not send ECS. Cloudflare's reasoning: ECS leaks a /24-precision hint about the end user's network to every authoritative server they query. That's a privacy regression for the user. The tradeoff is slightly less precise CDN routing.

Google's 8.8.8.8 does send ECS by default. Some authoritatives use it, some ignore it, some require it. The example uses 8.8.8.8 precisely because Cloudflare wouldn't honor +subnet even if you sent it.

Read deeper

  • RFC 7871: EDNS Client Subnet
  • Cloudflare's blog post on why 1.1.1.1 doesn't support ECS (search: "Cloudflare 1.1.1.1 ECS")

<a id="dnssec-valid-vs-cd"></a>

DNSSEC: valid vs forced#

▢️ Run live: isitdns.net/#dig?preset=dnssec-valid-vs-cd. Click to load and auto-run this chip on the front-page tool.

dig dnssec-failed.org A @1.1.1.1 +dnssec
dig dnssec-failed.org A @1.1.1.1 +dnssec +cd

nslookup can't show this here, use dig. nslookup has no equivalent of +cd (the Checking Disabled bit) and cannot inspect or set DNSSEC header flags.

What you'll see

  • First query (+dnssec only) β†’ SERVFAIL. The resolver validated, the signatures are deliberately broken, and the resolver refused to lie. This is the correct behavior.
  • Second query (+dnssec +cd) β†’ NOERROR with the answer. +cd ("Checking Disabled") tells the resolver "skip validation; just give me the bytes."

Live proof: SERVFAIL via DoH

curl -s -H 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=dnssec-failed.org&type=A'
{
  "Status": 2,
  "AD": false,
  "Question": [{"name": "dnssec-failed.org", "type": 1}],
  "Comment": ["EDE(9): DNSKEY Missing no SEP matching the DS found for dnssec-failed.org."]
}

"Status": 2 is SERVFAIL. The Extended DNS Error (EDE, RFC 8914) code 9 tells you exactly why: no DNSKEY with a matching DS entry (the key is wrong, not just missing). This is the expected behavior of a validating resolver.

Why this matters

dnssec-failed.org is a public fixture zone with intentionally broken DNSSEC signatures. The zone's operators maintain it specifically so DNS engineers can verify that their validating resolvers do the right thing: refuse the answer rather than serve a potentially forged record.

When you query without +cd, your validating resolver:

  1. Fetches the answer
  2. Fetches the RRSIG covering it
  3. Fetches the DNSKEY that signed the RRSIG
  4. Fetches the DS at the parent (.org)
  5. Hashes the DNSKEY and compares to the DS
  6. Discovers the hashes don't match
  7. Returns SERVFAIL instead of the (untrustworthy) answer

The +cd flag short-circuits all of that. CD stands for "Checking Disabled" and lives in the DNS header. The client is saying, "I take responsibility for any validation; just hand me the raw response from the authoritative." This is useful for debugging ("is the failure in the signature or in the resolver?") and for tools that do their own validation downstream.

                    +dnssec only           +dnssec +cd
                    ─────────────          ───────────
client asks:        validate, please       don't bother
resolver acts:      walks chain, fails     forwards raw answer
client receives:    SERVFAIL               NOERROR + (bad) answer

teaching point:     this is the correct    this proves the signature
                    failure mode for       is the problem, not the
                    DNSSEC                 resolver

Common debugging dance

If +dnssec gives SERVFAIL and +dnssec +cd gives the answer, your zone is bogus (the signature is broken somewhere). If both give SERVFAIL, your resolver itself isn't returning the data, which is a different bug, usually upstream connectivity.

Read deeper

  • DNSSEC troubleshooting: the full diagnosis flow with EDE codes
  • DNSViz: visualize the whole signature chain (the visual companion to this debugging)
  • RFC 4035 Β§3.2.2 and Β§4.6: the CD bit and how resolvers handle it
  • dnssec-failed.org is run as a public service for exactly this kind of testing

<a id="ptr-reverse-dns"></a>

PTR and reverse DNS#

▢️ Run live: isitdns.net/#dig?preset=ptr-reverse-dns. Click to load and auto-run this chip on the front-page tool.

dig 2.1.1.1.in-addr.arpa PTR @1.1.1.1
nslookup 1.1.1.2 1.1.1.1

What you'll see

  • Status: NOERROR
  • Answer: a PTR record pointing at security.cloudflare-dns.com.
curl -s -H 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=2.1.1.1.in-addr.arpa&type=PTR'
{
  "Status": 0,
  "Question": [{"name": "2.1.1.1.in-addr.arpa", "type": 12}],
  "Answer": [{"name": "2.1.1.1.in-addr.arpa", "type": 12, "TTL": 176,
              "data": "security.cloudflare-dns.com."}]
}

Why this matters

PTR records answer the question "what name lives at this IP?" The encoding is the part that trips people up: the IP's octets get reversed and .in-addr.arpa appended.

forward DNS (right to left):
   www.example.com.        β†’  com β†’ example β†’ www
                              TLD   2nd-level  hostname

reverse: flip the octets first, then it reads right to left like any name:
   1.1.1.2   β†’   2.1.1.1.in-addr.arpa.
   the host octet .2 moves to the front (1.1.1.2 reversed is 2.1.1.1)

Why the reversal? DNS is hierarchical right-to-left. The TLD lives at the right and you walk leftward to the specific name. IPv4 addresses are hierarchical left-to-right (network β†’ host). Reversing the octets aligns IPv4's hierarchy with DNS's.

For IPv6 the suffix is .ip6.arpa and the encoding splits each byte into two nibbles, also reversed: 2001:db8::1 becomes 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.

Who owns PTR records?

PTR records for a public IP are typically owned by the operator who controls the IP block, not the operator of the service running on that IP. Your laptop's reverse DNS usually reads something like c-71-238-104-185.hsd1.or.comcast.net (a real, live PTR) because Comcast controls the surrounding address block and assigns auto-generated names to it. The website your laptop is talking to has no say in what its end-user PTRs look like.

The flip side: a working PTR is required by some receiving mail servers. Sending mail from an IP whose PTR doesn't match the HELO name is one of the top causes of inbound mail rejection. If you operate a mail server, you usually have to ask your hosting provider to set the PTR specifically for that purpose. See also: MX records and the email delivery chain.

Read deeper


Root nameservers#

▢️ Run live: isitdns.net/#dig?preset=root-nameservers. Click to load and auto-run this chip on the front-page tool.

dig . NS @1.1.1.1
nslookup -type=NS . 1.1.1.1

What you'll see

  • Status: NOERROR
  • 13 NS records: a.root-servers.net through m.root-servers.net
  • These are the entry points for every DNS resolution worldwide

Why this matters

Every recursive resolution starts at the root. A fresh recursive (no cache) asks a root server "where do I find .com?" The root replies "go ask Verisign," and the recursive continues down. The "13 root servers" are the bootstrap. See NS records for how delegations work.

   . (the root zone: roughly 1,400 TLD delegations + DNSSEC, ~2 MB)
   β”‚   The root hints file readers usually mean is much smaller (~3 KB,
   β”‚   just the 13 NS records and their glue).
   β”‚
   β”œβ”€β”€ 13 named identities: a.root-servers.net through m.root-servers.net
   β”‚
   β”œβ”€β”€ Each is actually anycast: about 2,000 physical instances worldwide
   β”‚   across the 13, all answering at the same IPs. Your packet goes to
   β”‚   whichever is topologically closest to you.
   β”‚
   └── Operated by 12 organizations
       (Verisign runs both A and J; everyone else runs one)

Why 13?

The number 13 dates to the UDP DNS message size limit (512 bytes) before EDNS0 expanded it. 13 NS records was the maximum that fit in a single UDP packet alongside the question section, header, and glue. Today EDNS0 makes that limit obsolete, but the 13-server tradition stayed.

Each of the 13 "servers" is really a fleet of anycast instances; the counts vary widely by letter, and the whole system stood at just over 2,000 operational instances as of July 2026 (root-servers.org publishes the live count and per-instance maps). When you query a.root-servers.net, your packet goes to whichever instance is topologically closest to your network. The id.server CHAOS-class TXT query reveals which instance answered (RFC 4892).

Why these are stable

The full root zone has every TLD delegation (~1,400 entries) plus DNSSEC records, about 2 MB today. What most operators mean by "the root data" is the small bootstrap root hints file (~3 KB, the 13 NS names + their glue). Most recursives ship with the hints file, query the public roots on cold cache, and let TTLs handle the rest. RFC 8806 (obsoletes RFC 7706) formalizes serving the full root zone locally on the recursive itself for sites that want to avoid hitting the public roots at all.

Read deeper


DoH vs DoT#

▢️ Run live: isitdns.net/#dig?preset=doh-vs-dot. Click to load and auto-run this chip on the front-page tool.

dig +https @1.1.1.1 iana.org A
kdig +tls @1.1.1.1 iana.org A

These commands require BIND 9.18 or later for +https and knot-dnsutils for kdig. The macOS system dig (9.10.x) does not support either flag. See the prose note below.

nslookup can't show this here, use dig (or kdig). nslookup has no support for DoH or DoT transport.

What you'll see

Identical answers in both responses: same RRset, same TTL behavior. Only the envelope differs. The DNS message inside is byte-for-byte the same as a plain Do53 query.

Why this matters

DoH and DoT exist to encrypt the DNS wire so observers on the network path can't read your queries. They carry the exact same DNS message (RFC 1035 binary); the only difference is what wraps it.

DoT (port 853):
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ TLS frame                       β”‚
   β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
   β”‚  β”‚ DNS wire format (RFC1035) β”‚  β”‚
   β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

DoH (port 443):
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ TLS frame                                β”‚
   β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
   β”‚  β”‚ HTTPS request                      β”‚  β”‚
   β”‚  β”‚  POST /dns-query                   β”‚  β”‚
   β”‚  β”‚  Content-Type: app/dns-message     β”‚  β”‚
   β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚
   β”‚  β”‚  β”‚ DNS wire format (RFC1035)    β”‚  β”‚  β”‚
   β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚
   β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
DoHDoT
Port443853
WrappingHTTPS request inside TLSDNS message directly inside TLS
Blends withregular web trafficnothing; port 853 is the giveaway
Easy to blockhard (looks like HTTPS)easy (firewall port 853)

Why one over the other?

DoH was originally designed for the browser case. Firefox and Chrome can use their existing HTTPS stack without opening a separate TLS connection on port 853. From the network's perspective, DoH is indistinguishable from any other HTTPS connection without deep packet inspection.

DoT predates DoH (RFC 7858 in 2016 vs RFC 8484 in 2018) and is friendlier to dedicated DNS appliances and tools like Pi-hole that already have a TLS stack but not a full HTTPS one. The dedicated port also makes it easier for a network admin to see that DoT is happening, which is sometimes useful and sometimes the point of using DoH instead.

ODoH (Oblivious DoH, RFC 9230) is the next layer: it introduces a proxy between the client and the resolver so the resolver can't link the client IP to the query content. Limited deployment so far.

Important: stock dig may not support either

The standard macOS dig is BIND 9.10.x and does not support +tls or +https. BIND 9.18 added them. If dig +tls gives an option error, your dig is too old. Install kdig as the canonical CLI for encrypted DNS testing: on Debian/Ubuntu apt install knot-dnsutils, on macOS brew install knot. The application/dns-message content-type abbreviated in the DoH diagram above is the canonical name from RFC 8484; the diagram shortens it to fit.

Read deeper


<a id="family-filter"></a>

Family filter: blocked vs allowed#

▢️ Run live: isitdns.net/#dig?preset=family-filter. Click to load and auto-run this chip on the front-page tool.

dig protonvpn.com A @8.8.8.8
dig protonvpn.com A @185.228.168.168
nslookup -type=A protonvpn.com 8.8.8.8
nslookup -type=A protonvpn.com 185.228.168.168

What you'll see

  • Google β†’ the real answer: protonvpn.com resolves to 185.159.159.140, in ProtonVPN's own RIPE-registered block (185.159.159.0/24, netname ProtonVPN-CH1)
  • CleanBrowsing Family β†’ NXDOMAIN (verified 2026-07-17: the authority section carries an RPZ-style SOA, cleanbrowsing.rpz.noc.org., a fingerprint that the "no" was manufactured by the filter rather than by the real zone). Some filtering operators return a block-page IP instead. Block lists and mechanisms change, and results can vary by network vantage. The live SPA chip shows current behavior from the isitdns.net edge vantage; what you see from your own terminal may differ.

Why this matters

A "family safe" or "content filtering" resolver doesn't inspect HTTP traffic and decide whether to serve a page. It works one layer lower: it refuses to resolve the DNS name. The block list isn't just adult content. VPN providers, privacy tools, and proxy services are commonly on family/safe-mode lists too. The chip uses protonvpn.com as a representative example of the VPN-blocking category, not the adult-content category many readers assume. The client never gets an IP, so the browser can't even attempt the connection. From the user's perspective, the site simply doesn't exist.

            β”Œβ”€β”€ operator's filter list contains the name? ──┐
            β”‚                                                β”‚
   YES β†’ choose mechanism:                              NO β†’ forward
            β”‚                                                  upstream
            β”œβ”€β”€ (a) return NXDOMAIN
            β”‚       client sees "name doesn't exist"
            β”‚       browser: connection error
            β”‚
            β”œβ”€β”€ (b) return 0.0.0.0
            β”‚       client connects to 0.0.0.0 β†’ fails immediately
            β”‚       browser: connection error
            β”‚
            └── (c) return operator's block-page IP
                    client connects β†’ operator's web server
                    browser: shows an explanation page

CleanBrowsing's Family endpoint currently uses mechanism (a) for this domain: NXDOMAIN, the cleanest response (the name "didn't exist"). Some operators return their own block-page IP so the user sees a "this site is blocked" page rather than a generic "site unreachable" error. Both are legitimate filter mechanics; they just teach different things.

Reliability and bypass

DNS-level filtering is fast, cheap, and works for every device on the network without per-device configuration. It is also trivially bypassed by anyone who can change their DNS resolver: configure your laptop to use 1.1.1.1 and the filter is gone. This is why DNS filtering is a first line of defense, not a strong control. Enterprise filtering layers add network-level enforcement (block port 53 outbound, force traffic through the filtering resolver) to close the bypass.

Filter operators worth knowing

OperatorDefault filtering
1.1.1.1 (Cloudflare)None
1.1.1.2 (Cloudflare for Families: malware)Malware/phishing
1.1.1.3 (Cloudflare for Families: adult)Malware + adult
8.8.8.8 (Google)None
9.9.9.9 (Quad9)Malware/phishing only (no morality filter)
185.228.168.168 (CleanBrowsing Family)Adult content + VPNs/proxies
94.140.14.15 (AdGuard DNS Family)Ads + adult

Read deeper


<a id="ad-blocking-adguard"></a>

Ad blocking: AdGuard vs Google#

▢️ Run live: isitdns.net/#dig?preset=ad-blocking-adguard. Click to load and auto-run this chip on the front-page tool.

dig adnxs.com A @8.8.8.8
dig adnxs.com A @94.140.14.14
nslookup -type=A adnxs.com 8.8.8.8
nslookup -type=A adnxs.com 94.140.14.14

What you'll see

  • Google β†’ real IPs in the 68.67.x.x range (Microsoft Advertising / Xandr ad infrastructure). The ad would load.
  • AdGuard β†’ NOERROR with a single A record of 0.0.0.0. AdGuard's default resolver blocks adnxs.com by returning the sinkhole address rather than a real IP. It also tags the response with Extended DNS Error 17, "Filtered" (RFC 8914 Β§4.18), the standards-track way for a resolver to admit "I blocked this on purpose"; dig prints it as EDE: 17 (Filtered) in the OPT pseudosection. Block lists change over time; if adnxs.com cycles off AdGuard's default list you may see a real IP instead. The live SPA chip shows current behavior from the isitdns.net edge vantage.

Why this matters

Sinkhole DNS for ad blocking is one of the oldest and still most effective tricks in the privacy toolkit. Instead of inspecting and rewriting HTTP traffic (which is expensive and breaks with TLS), the resolver simply returns a dead answer (0.0.0.0, or for some operators NXDOMAIN) for known ad/tracker domains. The browser receives the answer, fails to connect, and no traffic reaches the ad server.

   adnxs.com A @8.8.8.8              adnxs.com A @94.140.14.14
   (unfiltered)                       (AdGuard sinkhole)
        β”‚                                      β”‚
        β–Ό                                      β–Ό
   68.67.x.x                              0.0.0.0
   (real ad server)                        (sinkhole address)
        β”‚                                      β”‚
   client TCP β†’ ad fetched               client: connect to 0.0.0.0 fails
   ad displayed                          ad slot stays empty
   pixel tracks user                     no traffic to ad server

"What if I run this and don't see a sinkholed response?"

If you run the AdGuard query locally and get back real IPs, two explanations are likely. First, your network may be intercepting DNS before it reaches AdGuard. Common culprits:

  • Captive portals that hijack port 53 to force you through their auth page
  • Hotel/cafe WiFi that runs its own resolver and ignores your @94.140.14.14
  • Some ISPs that transparently redirect port 53 traffic to their own resolver
  • Corporate networks with split-horizon DNS

Second, adnxs.com may have cycled off AdGuard's default block list. Block lists are live documents. The SPA's vantage runs from a Cloudflare edge worker where there is no interception, and the chip reflects the current state of AdGuard's list at query time.

Why adnxs.com?

adnxs.com is the ad-serving domain for Microsoft Advertising (formerly Xandr / AppNexus). It is a useful DNS-block test domain because:

  1. It serves only advertising traffic; no legitimate user-facing content lives at this name
  2. It appears across the major block lists precisely because it is a pure ad-network domain
  3. It is distinct from Google's own ad infrastructure, demonstrating that blocking applies to the whole ecosystem, not just one vendor

DNS-level vs browser-level ad blocking

DNS (Pi-hole, AdGuard DNS, NextDNS)Browser extension (uBlock, AdGuard)
CoverageEvery device on the network, every appOnly the browsers you install it on
GranularityWhole domain (all-or-nothing)Specific page elements
Blocks first-partyNo (same domain as content)Yes
ConfigurationNetwork-level, centralPer-device, per-browser

The two are complementary, not competing. Pi-hole users typically also run uBlock for the cases DNS can't catch.

Read deeper

  • Pi-hole project: the most popular self-hosted DNS sinkhole
  • AdGuard Home: same idea, different codebase
  • EasyList: the rule list most ad blockers use

<a id="ad-blocking-controld"></a>

Ad blocking: Control D vs Google#

▢️ Run live: isitdns.net/#dig?preset=ad-blocking-controld. Click to load and auto-run this chip on the front-page tool.

dig googlesyndication.com A @8.8.8.8
dig googlesyndication.com A @76.76.2.2
nslookup -type=A googlesyndication.com 8.8.8.8
nslookup -type=A googlesyndication.com 76.76.2.2

What you'll see

  • Google β†’ real IPs for the ad-delivery domain (Google AdSense delivers ads through googlesyndication.com)
  • Control D β†’ NOERROR with a single A record of 0.0.0.0. The 76.76.2.2 free profile (ads and tracking blocking) sinkholes googlesyndication.com as a known ad-serving domain. Note that 76.76.2.0 is Control D's free unfiltered profile; querying it returns real, unfiltered IPs (the exact addresses often differ from what Google hands you, because googlesyndication.com is itself geo-load-balanced and each resolver gets an answer for its own vantage). Block lists change; if the result differs when you run it, the live SPA chip shows current behavior from the isitdns.net edge vantage.

Same mechanism as the AdGuard example: a blocked answer that makes the client connection fail before it leaves the network.

Why this matters (beyond "it's another adblocker")

googlesyndication.com is the domain Google uses to deliver AdSense ads on third-party websites. Blocking it at DNS removes ads from every site that uses Google's ad network without touching any of the actual page content.

That makes it a cleaner test than the apex doubleclick.net. DoubleClick's apex resolves even on some filtering resolvers; googlesyndication.com is a purpose-built ad-delivery endpoint and appears on more aggressive block lists. Whether an operator blocks it tells you something about how thorough their list is.

Control D's profile system

Control D is part of a newer wave of configurable resolvers (NextDNS and AdGuard DNS are in the same space; Quad9 offers fixed policy variants on different IPs instead). The differentiator is per-account custom block rules. Each user gets their own profile (different IPs per profile), and the profile decides what's blocked. The 76.76.2.2 IP shown here is the free ads and tracking blocking profile; 76.76.2.0 is the free unfiltered profile, and their other profiles have different defaults.

OperatorPer-account profilesOne-policy-for-all
Control DYesNo
NextDNSYesNo
AdGuard DNSYes (with account)Yes (anonymous)
Quad9NoYes
Cloudflare FamilyNoYes

The tech is the same DNS sinkhole trick. The product differentiation is block-list curation and per-account customization.

Read deeper

  • Control D docs at docs.controld.com
  • NextDNS, AdGuard DNS for comparison

<a id="any-rfc-8482"></a>

ANY and RFC 8482#

▢️ Run live: isitdns.net/#dig?preset=any-rfc-8482. Click to load and auto-run this chip on the front-page tool.

dig cloudflare.com ANY @ns3.cloudflare.com
dig google.com ANY @ns1.google.com
nslookup -type=ANY cloudflare.com ns3.cloudflare.com
nslookup -type=ANY google.com ns1.google.com

(ns3.cloudflare.com is one of the live nameservers for cloudflare.com; dig cloudflare.com NS shows the current set, ns3 through ns7. Any of them works here.)

What you'll see

The two operators made opposite choices, and that split is the whole lesson.

Cloudflare's authoritative answers the ANY query with a single minimal record, HINFO "RFC8482" "", instead of enumerating the zone's records (add +dnssec and the RRSIG covering that HINFO comes back too). Google's authoritative returns the actual record set for google.com: A, AAAA, MX, TXT, NS, SOA, and more. Same query type, two deliberate policies. The RFC 8482 minimal-HINFO response is an option operators may implement, not a universal standard, so what you get for ANY depends entirely on who runs the authoritative.

Live proof: cloudflare.com ANY

dig cloudflare.com ANY @ns3.cloudflare.com +dnssec
;; ANSWER SECTION:
cloudflare.com.   3600   IN   HINFO   "RFC8482" ""
cloudflare.com.   3600   IN   RRSIG   HINFO 13 2 3600 ...

That is the entire answer. No A, no AAAA, no MX, no TXT. The server even spells out its reasoning in an Extended DNS Error on the response: EDE: 21 (Not Supported): (Type ANY Queries not supported here, RFC8482). The HINFO "RFC8482" "" record is the server's polite way of saying "I decline to enumerate everything for ANY; ask for the type you actually want." The RRSIG over the HINFO (returned when the query sets the DNSSEC-OK bit, which is what +dnssec does; without it you get the bare HINFO) is worth noticing: the zone signs even its refusal, so a validating resolver can prove the minimal answer really came from the zone owner.

Live proof: google.com ANY (abbreviated; A/AAAA values vary by anycast site)

dig google.com ANY @ns1.google.com
;; ANSWER SECTION:
google.com.   300   IN   A       142.251.214.110
google.com.   300   IN   AAAA    2607:f8b0:4007:80c::200e
google.com.   300   IN   MX      10 smtp.google.com.
google.com.   300   IN   TXT     "v=spf1 include:_spf.google.com ~all"
google.com.   60     IN   SOA     ns1.google.com. dns-admin.google.com. ...
google.com.   345600 IN   NS      ns1.google.com.

Note: Cloudflare's recursive resolver (1.1.1.1) refuses ANY outright on every transport: NOTIMP (status 4) with EDE code 21 "Not Supported", whether you ask over plain port 53 or DoH (verified from an external probe and via DoH on 2026-07-17). If a query to 1.1.1.1 ever appears to answer ANY from cache, a middlebox on your path is answering, not 1.1.1.1. The recursive's refusal and the authoritative's minimal HINFO are two different behaviors at two different layers: the recursive resolver's policy versus the authoritative's record set. Querying the authoritative directly removes the recursive layer from the picture.

Why this matters: ANY was a DDoS weapon

The ANY query type historically meant "give me every record you have for this name." In the modern internet it became a powerful tool for DNS amplification attacks:

ATTACK (pre-RFC 8482):
  attacker spoofs source = victim.example, sends:
    "ANY example.org" (60 bytes) ──▢ open recursive
  recursive forwards to authoritative:
    ANY example.org (60 bytes) ──▢ auth server
  auth returns full answer set (~3000 bytes) ──▢ recursive
  recursive returns ~3000 bytes ──▢ victim.example

  60-byte query β†’ 3000-byte response β†’ 50Γ— amplification
  Repeat from thousands of open recursives β†’ gigabits/sec at the victim

The attacker sends a small UDP query with a spoofed source IP (the victim's). The authoritative server returns a much larger response, which gets sent to the victim. Multiply by thousands of source resolvers and you have a meaningful DDoS, all powered by the legitimate ANY behavior.

The fix: RFC 8482

RFC 8482 (published 2019) lets authoritative servers refuse ANY queries by returning a minimal placeholder: a single HINFO record with the value "RFC8482" "". The response shrinks from thousands of bytes to roughly the size of the query, the amplification factor collapses from ~50Γ— to ~1-2Γ—, and the attack stops being profitable. (Measured live: Cloudflare's full minimal response is 124 bytes on the wire, EDNS options included.)

post-RFC 8482 (minimal HINFO):
  attacker: ANY example.com (60 bytes) ──▢ authoritative
  authoritative returns:
    example.com. HINFO "RFC8482" "" (~100-byte message)
  amplification ~1-2Γ—; attack is no longer profitable

The RFC explicitly does not require this behavior, it just allows it. Different operators made different choices:

  • Cloudflare returns the minimal HINFO "RFC8482" "" for zones they serve (the live behavior, shown above); they championed the RFC and their blog post "RFC8482 - Saying goodbye to ANY" explains why
  • Google chose backward compatibility and returns the full set for google.com
  • Most managed DNS providers went one way or the other based on their threat model

Why query the authoritative directly?

RFC 8482 is an authoritative-server behavior, so the commands above hit each zone's authoritative NS to see what it actually returns for ANY. Through a recursive resolver the result is cache-dependent: if the recursive has recently answered other record types for that name, it may serve the cached records rather than forward the ANY upstream. Querying the authoritative removes that ambiguity and shows the operator's actual choice. The zone's authoritative is the decision point: neither the recursive resolver nor the client can override what the authoritative returns for ANY.

What replaced ANY for legitimate use

People used ANY for zone enumeration. The modern replacements:

  • AXFR (zone transfer) if the operator allows it: usually they don't, except for internal secondaries. See zone transfers.
  • NSEC walking on DNSSEC-signed zones: enumerate the zone by following NSEC chains
  • Just ask for each type explicitly: dig name A; dig name AAAA; dig name MX; ...

Read deeper


Compare all (MX)#

▢️ Run live: isitdns.net/#dig?preset=compare-all-mx. Click to load and auto-run this chip on the front-page tool.

# Conceptually (pseudo-code):
for r in 1.1.1.1 8.8.8.8 9.9.9.9 208.67.222.222 ...; do
  dig example.com MX @$r
done

What you'll see

  • The same MX records from every resolver
  • Different latencies, that's the interesting variable
  • All resolvers agree on the answer because MX isn't operator-policy filtered like adult/ad/family lists

▢️ Both IP families: the SPA also supports running each resolver over IPv4 + IPv6 in parallel. Pick Both in the IP family dropdown to see whether v4 and v6 paths to the same resolver behave differently. Often they don't; when they do, it's usually a routing / anycast asymmetry worth investigating.

Why this matters

Asking the same question against many resolvers tells you two things at once:

  1. Latency comparison from your network. Running the same query at the same instant from the same vantage lets you rank resolvers by speed for your specific network path. The "fastest resolver" in benchmarks may not be the fastest for you.
  2. Sanity check on the answer. If every resolver in the panel agrees on the MX records, the answer is reliable. If they disagree, you've found something interesting (mid-propagation, anycast split, operator filter, mid-failover, broken zone).
                 dig example.com MX
                        β”‚
                        β”œβ”€β”€β–Ά @1.1.1.1   (Cloudflare)   18ms
                        β”œβ”€β”€β–Ά @8.8.8.8   (Google)       12ms
                        β”œβ”€β”€β–Ά @9.9.9.9   (Quad9)        22ms
                        β”œβ”€β”€β–Ά @208.67.222.222 (OpenDNS) 35ms
                        └──▢ @<more...>

                  Same MX records back from all
                  (MX records aren't filterable)

                  Latency tells you what's fast for YOUR network

How to read the latency numbers

Resolver latency from any one vantage depends on three things stacked together:

  1. Your ISP's path to each resolver's anycast endpoint. Different ISPs have different peering arrangements with different DNS operators.
  2. The resolver's own anycast topology. Some operators have an instance in every major metro; some have fewer.
  3. The current load and cache state of the specific instance you reached.

Small differences (5-20ms) are mostly noise. Big differences (100ms+) usually mean one of these:

  • Your ISP doesn't peer well with that operator
  • The resolver's anycast instance for your region is far away (rare for the big operators)
  • The query missed cache and the resolver had to walk the chain (will recover within seconds)

When to dig deeper

For real benchmarking, single-query results are too noisy. Use a tool that batches: dnsperf, dnsbench, or a continuous monitoring tool (which is what isitdns.net is). One query is a snapshot; sustained measurements tell you something useful.

Read deeper


How the chips become published lessons#

The chips on isitdns.net/#dig and the sections on this page are kept in sync deliberately. When a chip is added or changed, this page is updated to match. Each chip's ?preset=<slug> parameter maps directly to a section anchor on this page.

If you ever run a chip and the live behavior diverges from what this page describes, the live behavior is the source of truth. Open an issue (or ping the doc team) and someone will reconcile. DNS is a living protocol; what blocks today may not block tomorrow.

How this page fits the DNS wiki#

Two layers sit behind every example here, and they do different jobs.

This page is the worked-examples layer. One runnable demo per concept, the real answer it returns, and an annotation of what just happened. It is built to be skimmed and clicked. It answers "show me."

The DNS wiki is the reference layer. A page per record type, a page per zone type, and a page per security mechanism (DNSSEC, encrypted DNS, CAA, reverse DNS), plus the fundamentals: how a query resolves, how a response is assembled, recursive versus iterative. It answers "now explain the whole model."

The bridge between the two is the Read deeper list at the end of every section above. Each demo points at the wiki page that covers the same topic in full, so the path through the site is deliberate:

front-page chip  ──▢  this page (worked example)  ──▢  wiki page (full reference)
     run it             what just happened              the complete model

Click the DNSSEC chip, land on DNSSEC validated, watch the ad flag come back on a live query, then follow Read deeper into the wiki for the signing chain, the troubleshooting flow, and the RFCs behind it. The example shows the behavior; the wiki explains why it behaves that way. You can also start from the wiki and come back here when you want to see a claim run for real: the link works in both directions.

The reference wiki rolls out in stages, so each Read deeper link goes live as its wiki page is published.

See also#

Just for fun#

A TXT value is displayed as one long escaped string; line breaks don't survive the round trip through resolvers and tools. You can fake them: store the physical lines joined by ~ and let the front-page dig tool un-fold them back into art. Plain dig shows the folded line; the tool draws it.

First time? (run it against every resolver β†—)

dig +short TXT itwasdns.example.isitdns.net

<pre class="hangman"><span class="h-frame">______________________________</span> <span class="h-frame"> | | |</span> <span class="h-frame"> | | |</span> <span class="h-frame">|</span> <span class="h-cyan">O</span> <span class="h-pink">O</span> <span class="h-cyan"> .------------. -|- </span><span class="h-pink"> -|- .------------.</span> <span class="h-cyan"> | first time? |&gt; | | </span><span class="h-pink"> | | &lt;| it was DNS |</span> <span class="h-cyan"> '------------' </span><span class="h-pink"> '------------'</span> <span class="h-frame"> |</span> <span class="h-frame"> ==================================</span> <span class="h-lime">it's always DNS.</span></pre>

Pointed at every public resolver at once, they all hand back the same record. Everyone is in agreement. It was DNS.

Bonus: Kirby dances by. No ~ trick needed for this one, it is a one-liner. (run it β†—)

dig +short TXT kirby.example.isitdns.net

<pre class="kirby-dance"><span class="k-lime">&lt;('-'&lt;)</span> <span class="k-orange">^('-')^</span> <span class="k-green">(&gt;'-')&gt;</span> <span class="k-red">&lt;('-')&gt;</span></pre>

The trick is generic: any TXT with a couple of ~ becomes a tiny canvas.


See also: dig-along Β· dig-flags Β· nslookup-and-dig Β· when-its-dns