Since 2024, Gmail and Yahoo have required bulk senders to publish a DMARC record. So I wanted to know how far the most popular domains on the internet have actually got. Do they publish DMARC? Do they enforce it, or just monitor? And how many have moved on to BIMI and MTA-STS?

On November 6, 2026 I looked up the public DNS records of the top 10,000 domains on the Tranco list, a research ranking of popular sites that is built to be hard to game. No email was sent and no website was fetched: these are only DNS lookups, the same ones any mail server makes.

The short version:

Of the 7,236 top domains that receive email Share
Publish a valid DMARC record 82.8%
Enforce it (p=quarantine or p=reject) 61.2%
Use p=reject 35.1%
Publish exactly one SPF record 92.7%
Publish a BIMI record 13.3%
Publish an MTA-STS record 4.0%

The rest of this post covers how the scan works, the parsing traps I hit, what stood out in the results, and how to check your own domain in a minute.

The method

The domains. The Tranco top 10,000 (list ID K9YYW, created November 5, 2026). One entry wasn't a valid hostname and was skipped.

The lookups. For each domain, six DNS queries:

Record Name queried What it tells you
MX example.com Whether the domain receives email at all
SPF TXT at example.com Which servers may send for it
DMARC TXT at _dmarc.example.com What receivers should do with mail that fails
BIMI TXT at default._bimi.example.com A brand logo for supporting inboxes
MTA-STS TXT at _mta-sts.example.com A policy asking senders to use verified TLS
TLS-RPT TXT at _smtp._tls.example.com Where to send TLS failure reports

The resolvers. Cloudflare (1.1.1.1) and Google (8.8.8.8), set explicitly so the result doesn't depend on my ISP's resolver.

The denominator. Percentages are of domains with at least one real MX record, because a DMARC policy matters most for domains that actually handle email. Of the 9,960 domains analysed, 7,236 have an MX record. The other 2,724 get their own section below.

Here is the core of the scanner, using only Node's built-in dns module:

import { Resolver } from "node:dns/promises"; const resolver = new Resolver({ timeout: 4000, tries: 3 }); resolver.setServers(["1.1.1.1", "8.8.8.8"]); // "No such record" is an answer. A timeout or SERVFAIL is not. const MISSING = new Set(["ENOTFOUND", "ENODATA"]); async function lookup(kind, name) { try { const value = kind === "mx" ? await resolver.resolveMx(name) : await resolver.resolveTxt(name); return { ok: true, value }; } catch (err) { if (MISSING.has(err.code)) return { ok: true, value: [] }; return { ok: false, code: err.code }; } } // A TXT record can be split into 255-byte strings. Join them before parsing. const records = (txt, prefix) => txt.map((chunks) => chunks.join("")).filter((t) => t.trim().toLowerCase().startsWith(prefix)); function tags(record) { const out = {}; for (const part of record.split(";")) { const i = part.indexOf("="); if (i > 0) out[part.slice(0, i).trim().toLowerCase()] = part.slice(i + 1).trim(); } return out; } async function scan(domain) { const [mx, apex, dmarc] = await Promise.all([ lookup("mx", domain), lookup("txt", domain), lookup("txt", `_dmarc.${domain}`), ]); const failed = [mx, apex, dmarc].find((r) => !r.ok); if (failed) return { domain, err: failed.code }; // excluded, not counted as "missing" const spf = records(apex.value, "v=spf1"); const dm = records(dmarc.value, "v=dmarc1"); return { domain, mx: mx.value.length > 0, spfCount: spf.length, // more than 1 = SPF permerror dmarcCount: dm.length, // more than 1 = invalid policy: dm.length === 1 ? (tags(dm[0]).p || "").toLowerCase() : null, }; } 

The full version adds BIMI, MTA-STS and TLS-RPT, RFC 7505 null MX detection and the SPF all qualifier. It runs 40 domains at a time and retries failures once at the end. All 10,000 domains took under four minutes.

Five traps worth knowing if you parse these records yourself

1. "No record" and "lookup failed" are different answers. ENOTFOUND and ENODATA mean the record doesn't exist. ESERVFAIL, EREFUSED and timeouts mean you don't know. If you count both as "no DMARC", you overstate how many domains lack it. 39 domains still failed after a retry (32 SERVFAIL, 6 FORMERR, 1 REFUSED), and I excluded them rather than guess.

2. TXT records come back in chunks. A long SPF or DMARC record is split into strings of up to 255 bytes, and resolveTxt returns each record as an array of those strings. Join them with no separator before parsing, or a long include: list gets cut in half.

3. More than one SPF record is not "extra SPF". RFC 7208 says a domain with two v=spf1 records gives a permanent error, so SPF fails for every email. 0.6% of the mail-receiving domains (46 domains) publish more than one.

4. A domain with no MX is not the same as one that refuses mail. An RFC 7505 "null MX" (0 .) states that a domain accepts no email. 74 domains in the list publish one. Most domains without email simply have no MX record at all.

5. Check your parser against a second source. I re-resolved a random sample of 40 domains through a third resolver (Quad9, 9.9.9.9), with a separate regex-based DMARC parser. The policy matched for all 40.

What the results show

Most top domains publish DMARC. Far fewer enforce it.

82.8% of mail-receiving domains publish a valid DMARC record, but the policy is where it gets interesting:

DMARC policy Share of mail-receiving domains
p=reject 35.1%
p=quarantine 26.1%
p=none 21.5%
No DMARC record 17.0%

p=none asks for reports but tells receivers to do nothing with failing mail, so an attacker can still put that domain in a From address. It's the right first step, and a lot of domains seem to have stayed on it.

The good news: 87.0% of domains with a DMARC record include a rua= address for aggregate reports, so most of them can see who is sending mail as their domain.

Bigger sites enforce more

Tranco rank Mail-receiving domains Enforcing DMARC p=reject
Top 100 65 75.4% 56.9%
101 to 1,000 623 74.8% 49.6%
1,001 to 10,000 6,548 59.8% 33.6%

That's 74.9% for the top 1,000 combined, against 59.8% for ranks 1,001 to 10,000. Since adoption drops the further down the list you go, it's probably lower again among the millions of domains that aren't in any top list.

SPF: an even split between strict and soft

Of the 6,710 mail-receiving domains with exactly one SPF record, 46.6% end it with -all (fail mail from unlisted servers) and 47.7% with ~all (soft fail). 2.1% use ?all, which tells receivers nothing, and 3 domains end with +all, which allows any server on the internet to send as them.

Top-level domains differ a lot

Among top-level domains with at least 100 mail-receiving domains in the list:

TLD Domains Valid DMARC Enforcing
.com 3,655 84.7% 66.0%
.ru 252 84.9% 68.7%
.org 364 83.2% 55.8%
.io 157 77.1% 56.1%
.de 166 86.1% 50.6%
.net 398 64.1% 42.0%
.jp 163 82.2% 39.3%
.edu 125 99.2% 62.4%

Nearly every .edu domain publishes DMARC, but more than a third of them (36.8%) stop at p=none. .jp is similar: a high adoption rate, with 42.9% on p=none.

Domains that send no email are the easiest to spoof

2,724 of the domains analysed have no MX record: CDNs, API hosts, asset domains, redirects. They don't send email, but that doesn't stop someone else from putting them in the From address of a phishing email.

The fix is two DNS records: an SPF record of v=spf1 -all and a DMARC record with p=reject. Only 16.4% of these domains have both.

BIMI and MTA-STS are still early

13.3% of mail-receiving domains publish a BIMI record, and 72.1% of those reference a mark certificate. MTA-STS (4.0%) and TLS-RPT (4.5%) are rare, even in the top 10,000.

Check your own domain in a minute

D=example.com dig +short MX $D # do you receive mail? dig +short TXT $D | grep -i spf1 # exactly one line, please dig +short TXT _dmarc.$D # p=none, quarantine or reject? dig +short TXT default._bimi.$D # optional: BIMI dig +short TXT _mta-sts.$D # optional: MTA-STS 

If you're not yet at p=reject, the usual path is:

  1. One SPF record that lists every service that sends for your domain.
  2. DKIM signing on your own domain, so it aligns with your From address.
  3. DMARC with p=none and a rua= address. Read the reports for a few weeks.
  4. When every legitimate sender passes, move to p=quarantine, then p=reject.
  5. For every domain you own that sends no email: v=spf1 -all and DMARC p=reject.

Limitations

  • It's one snapshot of public DNS from one location. Records change.
  • It checks records, not mail. It can't tell whether a domain's email actually passes, and DKIM keys can't be found without knowing each selector.
  • DMARC was read at the listed domain only. Subdomain policies (sp=) aren't analysed.
  • BIMI was checked at the default selector only, and MTA-STS policy files weren't fetched.
  • The top 10,000 are popular websites, not a random sample of all senders.

The full study, with every table, the methodology, a link for each finding and a CSV of the aggregate numbers, is on the Cresca site: DMARC adoption in 2026. Cresca is the email platform I'm building, and it walks you through SPF and DKIM for your own sending domain. If you'd like a breakdown I didn't include, ask in the comments and I'll run it against the same data.