DMARC setup guide for cold email (SPF, DKIM, DMARC in plain English)
Publish in this order, verify after each step, and count your SPF lookups before you add the fourth vendor. The exact records, with measured costs.
Publish these three records in this order: SPF, then DKIM, then DMARC at p=none with a reporting address, then enforce once the reports agree with you. Each step is verifiable before the next one, and doing them out of order is how domains end up with a p=reject policy rejecting their own legitimate mail. For a cold email domain the two steps that go wrong are almost always the SPF lookup budget and DMARC alignment, and both are measurable in a terminal in under a minute.
The order matters, and here is the whole sequence
DMARC does not authenticate anything by itself. It reads the results SPF and DKIM already produced and checks whether either covers the domain a human sees in the From header. Publishing it first therefore instructs receivers to enforce a policy against two mechanisms you have not yet confirmed work.
- SPF. One TXT record on the bare domain naming who may send as you. Verify with
dig, and count the lookups. - DKIM. A key published at a selector, generated by whichever platform will sign. Verify the selector resolves and the key is real.
- DMARC at
p=none, withrua. Monitoring only. Nothing is rejected. You are buying visibility. - Read a week of aggregate reports. Identify every source sending as your domain and whether each aligns.
- Move to
p=quarantine, thenp=reject. Only once step four holds no surprises.
Google's Email sender guidelines require bulk senders to publish DMARC and then say, in as many words, "Your DMARC enforcement policy can be set to none." So there is no external deadline pushing you past step three. The pressure to enforce early is self-imposed, and it is where the expensive mistakes live.
Step one: SPF, and the ten-lookup budget nobody counts
SPF is a TXT record on your bare domain listing the sending sources you authorise. The shape is unremarkable:
v=spf1 include:_spf.google.com ~all
The part that breaks real domains is RFC 7208 section 4.6.4, which caps the DNS-querying mechanisms evaluated during an SPF check at ten. The counted mechanisms are include, a, mx, ptr, exists and redirect, and they count recursively — every lookup performed inside an included record counts against your budget, not the vendor's. Exceed it and evaluation returns permerror, which most receivers treat as an outright failure. A record ending in a confident -all can authenticate nothing at all.
Nobody can count this by reading the record, because the cost of an include: is invisible until you resolve it. Measured on 9 September 2026:
include:_spf.google.com— 1. Google's record is currently flat:v=spf1 ip4:74.125.0.0/16 ip4:209.85.128.0/17plus IPv6 ranges,~all, with no nested includes. Advice that Workspace costs four lookups is describing an older record.include:spf.protection.outlook.com— 1. Also flat, ending-all.include:amazonses.com— 1. Flat, ending-all.include:sendgrid.net— 2. The record nestsinclude:ab.sendgrid.net.include:_spf.salesforce.com— 2. Its record isv=spf1 exists:%{i}._spf.mta.salesforce.com -all, and theexistsmacro is itself a querying mechanism.include:mailgun.org— 5. It nests_spf.mailgun.organd_spf.eu.mailgun.org, and the first of those nests_spf1.mailgun.organd_spf2.mailgun.org.include:zoho.com— 5. It nestsspf.zoho.com,zcsend.net,spf.zohomail.comandpopspf.zohomail.com.
Now assemble a record for a company running Google Workspace for staff mail, SendGrid for product notifications, Zoho for a support desk and Mailgun for a legacy application: 1 + 2 + 5 + 5 = 13. Three lookups over the limit, permerror at every compliant receiver, and every one of those four vendors is correctly configured. This is the most common serious SPF fault in existence and it is entirely invisible without counting.
Two further points on the terminator. -all is a hard fail and ~all a soft fail; receivers vary in how they treat a soft fail, and DMARC does not care which you use because it only reads pass or not-pass. What DMARC does care about is +all, which authorises the entire internet to send as you and turns SPF into decoration. And flattening a record — replacing includes with the IP ranges they currently resolve to — buys headroom at the price of a record that goes silently stale when a vendor renumbers.
Verify with one command: dig TXT yourdomain.com +short, then read the string beginning v=spf1 and count. If counting by hand is tedious, the SPF, DKIM and DMARC record generator builds the record and the SPF and DMARC record checker resolves the chain for you.
Step two: DKIM, and the two faults a checker will miss
DKIM publishes a public key at and signs each outgoing message with the matching private key. Your platform generates both and tells you what to publish — Google Workspace typically uses the selector google, Microsoft 365 uses selector1 and selector2 as CNAMEs, and Amazon SES Easy DKIM publishes three CNAMEs at random token names such as abc123xyz._domainkey.
That last case explains the fundamental limitation of every DKIM checker, including good ones. Selectors cannot be enumerated from DNS. There is no query that lists them; only a signature header reveals which selector signed a given message. So a checker either guesses from a list of well-known names or asks you. Autocloz probes about thirty well-known selectors and, crucially, reports a miss as unknown rather than fail — because "none of our guesses matched" is not evidence that a domain does not sign. Treating it as evidence once scored correctly configured SES, Microsoft 365 and Zoho domains as failures.
Two faults hide behind a record that exists and resolves:
- An empty
p=value means the key is revoked. RFC 6376 section 3.6.1 defines it that way, and operators use it to retire a selector gracefully. Any message still signed with that selector fails verification. A checker that only asserts the record's presence reports this as healthy. - A key under 1024 bits is weak. Sub-1024-bit RSA keys are factorable and major receivers reject them. 1024 is the floor and 2048 is the recommendation; a key length is readable directly from the base64
p=value without any external service.
Verify with dig TXT google._domainkey.yourdomain.com +short, substituting your platform's selector. An empty answer means the key was generated and never published, which is a five-minute fix that people spend weeks not finding. What DKIM signs and what it does not covers the mechanism if the signature model is new to you.
Autocloz's free plan covers 5 users and 10 mailboxes with continuous SPF, DKIM and DMARC monitoring on every sending domain — start free and let it re-check the records rather than remembering to.
Step three: the DMARC record, tag by tag, for a cold email domain
A first record for a domain that exists to send outbound mail and nothing else:
v=DMARC1; p=none; sp=none; adkim=r; aspf=r; rua=mailto:[email protected]
Published as a TXT record at _dmarc.yourdomain.com. Tag by tag, with the choices that are specific to a cold-email domain:
p— the policy for the domain itself.nonemeans take no action but report. Start here.sp— the policy for subdomains. Absent, subdomains inheritp. This matters more than it looks: a domain atp=rejectwithsp=noneprotects the root and leaves every subdomain spoofable, which is a combination that reads as strong and is not.np— the policy for non-existent subdomains, introduced by RFC 9989. For a cold-email domain with no subdomains at all,np=rejectis close to free protection: nothing legitimate sends from a subdomain that does not exist, so rejecting mail that claims to costs you nothing.adkimandaspf— alignment mode, relaxed (r, the default) or strict (s). Relaxed allows a subdomain of your organisational domain to satisfy alignment; strict requires an exact match. Start relaxed.rua— where aggregate reports go. Without it the record is a policy with no feedback, which defeats the point ofp=none.pct— do not use it. RFC 9989 removed the percentage tag entirely, replacing it with attag for test mode. Deployed receivers still honourpctper RFC 7489 section 6.3, where absence defaults to 100, so existing records keep working — butp=reject; pct=10means 90% of your mail is unprotected while the record advertises strength.
The remaining tags and what changed in the 2026 revision are covered in DMARC without the acronym soup, including the DNS tree walk that replaced the Public Suffix List.
Getting the reports somewhere you will actually read them
Sending rua to a mailbox on the sending domain works and is the simplest arrangement. Sending it to a third-party analytics service, or to a mailbox on a different domain you own, requires one extra record that is published by the destination, not by you.
RFC 9990 calls this verifying external destinations. When a receiver sees a rua address outside your organisational domain, it constructs a query by taking the destination host, prepending _report._dmarc, and prepending your policy domain. The RFC's own example: blue.example.com publishing rua=mailto:[email protected] causes a lookup of
blue.example.com._report._dmarc.red.example.net
and the TXT record found there must contain v=DMARC1 first in the tag list. If no valid record is found, "the URI MUST be ignored by the Mail Receiver generating the report". No error is surfaced to you. Reports simply never arrive, and the usual conclusion is that DMARC reporting does not work.
Two practical notes. If you use a hosted DMARC service, it publishes this record on its own domain and the arrangement is invisible to you — that is why it works out of the box. And Postmark runs a free DMARC aggregate-report digest, which Autocloz detects by looking for an @dmarc.postmarkapp.com address in your rua and then reports as monitoring being active on that domain.
Alignment is where cold email breaks, and here is what it looks like
A message can pass SPF and fail DMARC, and for cold email sent through a platform this is the default outcome rather than an edge case.
SPF authenticates the envelope sender — the address in MAIL FROM, which appears in the delivered message as Return-Path. Many sending platforms set that to their own bounce domain so they can collect delivery status notifications. The SPF check then passes for bounces.platform.com, which is entirely true and says nothing about you. DMARC looks for a passing result covering the domain in the visible From header, finds none, and fails.
You will see it in the Authentication-Results header, specified in RFC 8601, as spf=pass alongside dmarc=fail, with header.from= your domain and the SPF result naming someone else's. Send one message to a mailbox you control and read the raw source; reading your own authentication records end to end walks through every field.
There are exactly two fixes. Configure a custom Return-Path on a subdomain you control, so the envelope sender is yours and SPF aligns under relaxed mode. Or ensure DKIM signs with d= set to your own domain, since DMARC passes on either mechanism aligning. The second is usually easier and is why DKIM alignment carries most cold-email programmes. If you send through a relay such as SendGrid, both options are configuration on their side, and doing neither is what produces a domain that authenticates perfectly and still fails DMARC at every receiver.
When to move from none to quarantine to reject
The trigger is evidence, not elapsed time. Move to p=quarantine when a full week of aggregate reports shows every source you recognise passing with alignment, and no source you do not recognise sending meaningful volume. Move to p=reject after the same condition holds at quarantine.
Three things to check before each step. Every sending source is in the reports — invoicing systems, the help desk, the marketing platform, the CRM, the calendar tool. Forwarding is understood, because forwarded mail breaks SPF by design and survives on DKIM alone; a domain with SPF alignment but no DKIM will see forwarded mail fail at enforcement. And the reports are reaching you, which is the external-destination record above.
Microsoft is the strictest of the large receivers here and gives you a specific reason to get this right. Its rejection for a published enforcing policy reads 550 5.7.509 Access denied, sending domain does not pass DMARC verification and has a DMARC policy of reject — and note that this fires because of a policy you published, at any volume. It is a different fault from 550 5.7.515 Access denied, sending domain does not meet the required authentication level, which is Microsoft's high-volume authentication rule for domains sending more than 5,000 messages a day to Outlook, Hotmail and Live addresses, enforced since 5 May 2025. Same 5.7 prefix, different cause, and diagnosing one as the other sends you to the wrong DNS record.
What DMARC does not do for cold email, and what Autocloz does not do
DMARC is an anti-spoofing control. It stops other people sending as your domain. It does not improve placement for mail you send legitimately, it does not raise a reply rate, and a receiver that is filtering you for reputation will keep filtering you at p=reject. Authentication is a precondition, not a lever.
It also cannot see forwarded mail correctly. A mailing list or a forwarding rule rewrites enough of the message that SPF fails by design, and only a surviving DKIM signature saves it. That is a limitation of the model, not of any implementation.
And Autocloz specifically. Its domain checker resolves SPF recursively and fails a record over the ten-lookup limit, reads DKIM key length and revocation from the published key, and parses the DMARC p, sp, pct, adkim, aspf and rua tags — but it does not parse np, so a domain publishing a non-existent-subdomain policy gets no credit for it and no warning about its absence. Its DKIM probe guesses from a bounded list of about thirty selectors and reports a miss as unknown, which is honest and means a correct answer needs you to supply your own selector. It reads DNS, so it sees what you published rather than what a receiver concluded, and only the Authentication-Results header on a delivered message tells you the latter. It cannot enrol you in a DMARC reporting service on your behalf — it detects a Postmark reporting address in your record and shows the status. And no configuration of any of this makes placement certain; what it removes are the failures that are documented, measurable and yours to fix.
Frequently asked
What DMARC policy should a cold email domain start at?
Start at p=none with a working rua address, leave it there long enough to read a full week of aggregate reports, then move to quarantine and then reject once the reports show every legitimate source aligning. Google's sender guidelines accept p=none as satisfying the DMARC requirement for bulk senders, so there is no compliance reason to rush enforcement — and enforcing before you can see your own traffic is how a legitimate sending source gets silently rejected.
How many DNS lookups is my SPF record allowed?
Ten. RFC 7208 section 4.6.4 caps the DNS-querying mechanisms evaluated during an SPF check at ten, counting include, a, mx, ptr, exists and redirect, and counting them recursively through every included record. Exceeding the cap produces a permerror, which most receivers treat as a failure rather than a warning, so an SPF record that reads perfectly can authenticate nothing.
Why does my mail pass SPF but fail DMARC?
Because DMARC checks alignment, not just authentication. SPF authenticates the envelope return path in MAIL FROM, and a sending platform commonly sets that to its own bounce domain. The check passes for the platform's domain, which is true and says nothing about yours, so DMARC finds no result covering the domain in the visible From header and fails. The fix is either a custom Return-Path on your own domain or DKIM signing with a d= value that matches your From domain.
Do I need a separate DNS record to send DMARC reports to another domain?
Yes, and it is published by the receiving domain rather than by yours. RFC 9990 requires a receiver that sees a rua address outside your organisational domain to look up a TXT record at your-domain._report._dmarc.their-domain containing v=DMARC1. The RFC's own example is that blue.example.com specifying rua=mailto:[email protected] causes a query for blue.example.com._report._dmarc.red.example.net. Without that record the URI must be ignored and no reports are sent.
Is the pct tag still valid in DMARC?
It has been removed from the specification. RFC 9989, published in May 2026, obsoletes RFC 7489 and removes the percentage tag entirely, replacing it with a t tag for signalling test mode. Deployed receivers still honour pct as defined in RFC 7489 section 6.3, where an absent tag defaults to 100, so an existing record keeps working — but a new record should not rely on partial enforcement, and a p=reject with pct=10 leaves 90% of your mail unprotected.
What does a DKIM record with an empty p= mean?
The key has been revoked. RFC 6376 section 3.6.1 defines an empty p= value as a revoked public key, and operators use it to retire a selector without deleting the record. Any message still signed with that selector fails DKIM verification, so a live selector publishing an empty p= is a broken signing configuration rather than an absent one, and it is a fault a checker that only looks for the record's presence will report as healthy.