SPF, DKIM and DMARC for transactional email: what to publish and how each one breaks
Publish one SPF record that lists every system sending as your domain, switch on DKIM signing for each sending domain, and add a DMARC record at p=none with a reporting address. Then read the reports for a few weeks and tighten the policy only when every legitimate source shows up as passing. Gmail and Yahoo now expect this: all senders need SPF or DKIM, and bulk senders need all three. Provider guides keep returning to the same five failure causes: more than 10 DNS lookups in SPF, two SPF records, a DKIM key that was never published, a bounce domain that does not align with the From domain, and a DNS host that quietly rewrites CNAME values.
What Gmail and Yahoo require
The two big mailbox providers publish their rules, and they overlap closely. We read both pages on October 7, 2026: Google's email sender guidelines and Yahoo's sender requirements.
| Requirement | Google (Gmail) | Yahoo |
|---|---|---|
| All senders | SPF or DKIM | SPF or DKIM at a minimum |
| Bulk senders | SPF, DKIM and DMARC | SPF and DKIM, plus a valid DMARC policy of at least p=none that passes |
| Alignment | The authenticating domain must be the same domain as in the From header | The From domain must align with the SPF domain or the DKIM domain; relaxed alignment is acceptable |
| DKIM key length | 1024 bits or longer for personal Gmail accounts, 2048 recommended | 1024 bits minimum |
| Spam complaint rate | Keep below 0.10% in Postmaster Tools and never reach 0.30% | Keep below 0.3% |
| Unsubscribe | Marketing and subscribed messages must support one-click unsubscribe above 5,000 messages a day | Bulk senders need a list-unsubscribe header with one-click support, a visible link, and unsubscribes honored within 2 days |
| Sending IP | Valid PTR record with matching forward lookup; TLS for transmission | Valid forward and reverse DNS records for sending IPs |
Two details are easy to miss. Google's one-click rule is worded for marketing and subscribed messages, so a password reset is outside that sentence, but the authentication rows apply to every message. Google also tells you to keep messages of one category on one From address, such as sales receipts on one and promotions on another, and, if you send from several IPs, to give each message type its own; Yahoo asks you to segregate email types by IP or DKIM domain. If you send both kinds through one provider, give them different subdomains. The one-click headers Google specifies are List-Unsubscribe-Post: List-Unsubscribe=One-Click and a List-Unsubscribe URL.
SPF: the list of servers allowed to send
SPF is a TXT record on a domain that lists which hosts may send mail whose envelope sender, also called the MAIL FROM or Return-Path, belongs to that domain. The receiving server looks up that record, compares the connecting IP and returns pass, fail, softfail, neutral, none, temperror or permerror. The mechanism qualifiers are + (pass, the default), - (fail), ~ (softfail) and ? (neutral); that is why a record ends with ~all or -all.
; one SPF record per name, with every sender merged into it
mail.example.com. IN TXT "v=spf1 include:_spf.google.com include:mailgun.org ~all"
; wrong: two records on the same name make SPF evaluation fail
mail.example.com. IN TXT "v=spf1 include:_spf.google.com ~all"
mail.example.com. IN TXT "v=spf1 include:mailgun.org ~all" The 10-lookup limit
RFC 7208, section 4.6.4, requires receivers to limit the terms that cause DNS queries to 10 per evaluation: the include, a, mx, ptr and exists mechanisms and the redirect modifier. The all, ip4 and ip6 terms cost nothing. Going over the limit returns permerror, and an SPF check that ends in permerror cannot pass. Because includes nest, one include: for a large sender can consume several lookups on its own, and the sum covers everything reached while evaluating your record. The RFC adds a limit of two "void lookups", queries that return nothing, which also produces permerror when exceeded.
The practical consequences: count the terms before adding a new sender, replace a and mx with ip4 entries if you control the addresses, and split senders across subdomains. SPF is evaluated per domain, so mail.example.com for transactional mail and news.example.com for campaigns each get their own 10 lookups. A name that publishes two or more SPF TXT records is a permerror as well, which is why provider guides tell you to merge instead of adding. Mailgun's guide shows the merge: insert include:mailgun.org after v=spf1 and before ~all.
Why SPF passes and DMARC still fails
SPF checks the envelope sender, not the From address people see. If a provider uses its own bounce domain, SPF can pass for that domain and still say nothing about yours. Amazon SES, for instance, uses a subdomain of amazonses.com as the default MAIL FROM, which makes SPF validate automatically but ties it to Amazon's domain; a custom MAIL FROM on your own subdomain needs your own SPF TXT and an MX record (SES SPF documentation). Postmark makes the same point about its custom Return-Path: it is what lets SPF pass alignment. Until you configure it, DMARC depends on DKIM.
DKIM: a signature your domain publishes the key for
With DKIM, the sending service signs headers and body with a private key and adds a DKIM-Signature header naming a domain and a selector. The receiver fetches the public key from <selector>._domainkey.<domain> and verifies the signature. A pass proves the signed parts were not altered and that someone with the private key signed for that domain. Google asks for keys of at least 1024 bits and recommends 2048. Amazon SES adds a 2048-bit key by default for Easy DKIM and lets you pick 1024 bits instead, while limiting how often you can switch length (Easy DKIM).
; selector "selector1" on the sending subdomain
selector1._domainkey.mail.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
; a 2048-bit key is longer than one 255-character DNS string, so it is stored as
; several quoted strings inside the same record
selector1._domainkey.mail.example.com. IN TXT ( "v=DKIM1; k=rsa; p=MIIBIjANBg..." "...rest of the key..." ) Many providers now hand you CNAME records in place of the TXT. The provider hosts the key behind the CNAME, so it can rotate keys without asking you to edit DNS. SendGrid's automated security, Plunk's three DKIM CNAMEs and Mailgun's Automatic Sender Security all work this way. The cost is that a DNS host which appends your domain to the target, or proxies the record, breaks verification.
When DKIM fails after the record verifies, look for something that edits the message after signing: a gateway that appends a legal footer, a security product that rewrites links, or a forwarding system that changes the subject. The receiver reports a body hash mismatch because the bytes it checked are not the bytes that were signed.
DMARC: policy, alignment and reports
DMARC ties the two together. A message passes when SPF or DKIM passes and the passing domain aligns with the visible From domain, as Resend's DMARC guide puts it: either one is enough, and a message fails only when both fail. Alignment can be relaxed, where a subdomain and its organizational domain count as a match, or strict, where the names must be identical. The record lives at _dmarc.<domain> and its main tags are v, p (policy), rua (where aggregate reports go), pct (share of failing mail the policy applies to), sp (policy for subdomains), adkim and aspf (alignment modes) and ruf (forensic reports).
| Policy | What receivers do with mail that fails |
|---|---|
p=none | Deliver it normally and report on it |
p=quarantine | Treat it as suspicious, usually the spam folder |
p=reject | Refuse it during the SMTP conversation |
; week 0: observe only
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarcreports@example.com;"
; after the reports are clean: quarantine a share of failures, then all of them
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarcreports@example.com;"
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarcreports@example.com;"
; finally: reject
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarcreports@example.com;" Aggregate reports arrive as email with an XML attachment that lists the IP addresses sending as your domain and whether their mail passed SPF or DKIM. Read them like an inventory: the first weeks tend to surface a forgotten sender, such as a billing tool, a support desk or a staging server with production credentials. Plunk's guide suggests reviewing reports for a couple of weeks before moving past p=none; our suggestion is to move to quarantine with a small pct, raise it in steps while the failing share stays at senders you recognize, and treat reject as the last step. A subdomain without its own DMARC record falls back to the policy on the parent domain, or to sp if you set it, so a strict parent policy reaches every subdomain you forget about.
What each provider asks you to publish
| Provider | Records requested | Notes from its documentation |
|---|---|---|
| Resend | DKIM and SPF, as TXT plus MX or CNAME, on a domain you add | Recommends a subdomain; Return-Path defaults to a send subdomain; do not proxy the CNAME; often verifies in about 15 minutes, up to 72 hours; DMARC is a separate step |
| Postmark | DKIM TXT plus a custom Return-Path CNAME (pm_bounces to pm.mtasv.net by default) | Verification shows within 48 hours; the Return-Path record is what makes SPF align; DMARC is set up separately |
| SendGrid | With automated security on: a CNAME that supports SPF and DKIM, two link-branding CNAMEs and a DMARC TXT. Off: an MX plus SPF, DKIM and DMARC values you maintain | If the DNS host appends your domain to a CNAME value, verification fails and you must enter only the host part |
| Mailgun | SPF TXT, DKIM TXT, two MX records at priority 10 and an optional tracking CNAME | Merge include:mailgun.org into an existing SPF record; propagation can take 24 to 48 hours |
| Amazon SES | Easy DKIM records for the domain identity; optional custom MAIL FROM with MX and SPF TXT | 2048-bit key by default; Route 53 can create the records automatically |
Pricing and plan limits for these services are covered on Resend, Postmark, Mailgun and Amazon SES pages, and our Resend vs Postmark comparison shows how their domain models differ.
Troubleshooting by symptom
| What you see | Usual cause | Fix |
|---|---|---|
| SPF result is permerror | More than 10 lookups, or two SPF records on one name | Merge to one record, trim includes, move senders to separate subdomains |
| SPF passes, DMARC fails | The bounce domain does not align with the From domain | Configure the provider's custom Return-Path or MAIL FROM, or make sure DKIM aligns |
| DKIM shows none | Signing is not enabled or the selector record is missing | Query the selector name with dig and compare it to the provider's dashboard |
| DKIM fails with a body hash mismatch | Something modified the message after it was signed | Find the relay or footer injector and make the signer the last hop |
| Provider never marks the domain verified | Doubled domain in a CNAME value, or a proxied record | Enter the host part only, turn off proxying, then re-run verification |
| Gmail rejects with 5.7.26 | The message did not authenticate | Google says such mail may be marked spam or rejected; fix SPF and DKIM first |
| No aggregate reports | The rua address is not a working mailbox | Use a valid address that can receive XML attachments |
Query each record directly, then compare what you published with what the provider asked for. In a received message, open the raw headers and read the Authentication-Results line, which states the SPF, DKIM and DMARC verdicts and the domains that were checked.
dig TXT mail.example.com +short
dig TXT selector1._domainkey.mail.example.com +short
dig TXT _dmarc.example.com +short
dig CNAME em123.mail.example.com +short A rollout order that avoids surprises
- List every system that sends as your domain: the transactional API, marketing tool, support desk, CRM and any server that sends mail directly.
- Give each purpose its own subdomain, such as
mail.for receipts and resets, and register it at the provider. - Publish the provider's DKIM and SPF or Return-Path records and wait for verification; use the provider's checker, then send a test and read
Authentication-Results. - Add DMARC at
p=noneon the organizational domain with anruamailbox you actually read. - After a few weeks of clean reports, move to
quarantinein steps, then toreject. - Re-check after every vendor change. Adding a tool to SPF or enabling a new sender is the usual trigger for the next failure.
For the wider picture of reputation and inbox placement, read our deliverability guide, and for how domains interact with message types see transactional vs marketing email.
FAQ
Do I need DMARC if I only send transactional email?
Google and Yahoo make DMARC mandatory for bulk senders, and both recommend it for everyone because it also protects your domain from being forged. A monitoring-only record at p=none costs nothing and shows you who is sending as your domain.
Can I have more than one SPF record?
No. A domain must publish a single SPF record, and receivers return permerror when they find more than one. Combine every sender into one record or move each sender to its own subdomain.
What does p=none actually do?
It asks receivers to deliver messages normally and send you reports. It satisfies Yahoo's minimum policy for bulk senders but offers no protection against spoofing until you move to quarantine or reject.
Why does my provider want a subdomain?
A subdomain gives each kind of mail its own reputation, its own SPF lookup budget and its own DMARC data. Resend and Mailgun both recommend sending from one.