I checked the DMARC records of the top 10,000 domains. Only 61% enforce it
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
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:
- One SPF record that lists every service that sends for your domain.
- DKIM signing on your own domain, so it aligns with your From address.
- DMARC with
p=noneand arua=address. Read the reports for a few weeks. - When every legitimate sender passes, move to
p=quarantine, thenp=reject. - For every domain you own that sends no email:
v=spf1 -alland DMARCp=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
defaultselector 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.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.