Three records, one job each
The usual way this starts: transactional mail works for months, then support tickets appear. “Never got the confirmation email.” It’s in spam. Nothing in your code changed. What changed is that receivers stopped giving unauthenticated mail the benefit of the doubt, and since February 2024, Gmail and Yahoo enforce that with rejections instead of spam folders.
The three records answer three different questions. SPF: is this server allowed to send mail for this domain? DKIM: was this message signed by the domain, and did it arrive unmodified? DMARC: do either of those verdicts actually cover the domain in the From header the user sees, and what should the receiver do if not? Each is a TXT record. None of them is sufficient alone.
The one concept that makes all of it click: every email has two sender addresses. The envelope sender (called MAIL FROM in SMTP, shown as Return-Path in headers) that bounce messages go to, and the From header displayed in the mail client. They’re frequently different domains, especially when a service sends on your behalf. SPF checks the first one. Your user only ever sees the second one. DMARC exists to connect them.
SPF checks a domain you can’t see
An SPF record is a TXT record at the domain root, something like v=spf1 include:_spf.google.com ip4:203.0.113.7 -all. The receiving server takes the connecting IP, looks up the SPF record of the envelope domain, and checks whether the IP is on the list. The current spec is RFC 7208, from April 2014, after eight years as an experimental RFC.
The last mechanism decides what happens to everyone not on the list: ~all is softfail (mark it, deliver it, probably), -all is hard fail (reject it). In the DMARC era the practical difference has shrunk, receivers mostly feed the result into DMARC either way, but -all states clear intent and we’d use it once you’re confident the record is complete.
Two rules people learn the hard way. First: exactly one SPF record. Two v=spf1 records on the same domain is permerror per RFC 7208, and permerror means failed authentication, so the second record your newsletter tool told you to “just add” breaks the first one. Merge the includes into a single record. Second: SPF breaks on forwarding. When someone’s university address forwards to Gmail, the forwarding server delivers your message from its IP, which is not in your record, and SPF fails on completely legitimate mail. Sender rewriting (SRS) papers over this at some forwarders, not all. This is not fixable from your side, and it’s the main reason DKIM exists as the second path.
The 10-lookup limit fails silently
RFC 7208 caps SPF evaluation at 10 DNS lookups. Every include, a, mx, ptr, exists and redirect counts, including everything nested inside includes; ip4, ip6 and all are free. Lookup number 11 doesn’t degrade anything gracefully. The whole evaluation returns permerror, which downstream counts as an SPF fail.
The trap is that includes hide their cost. include:_spf.google.com alone is 4 lookups: the include itself plus the three _netblocks records nested inside it. Add Microsoft 365, an email service provider, a CRM and a helpdesk tool, and you’re over 10 without a single visible warning. Your record still looks fine in the DNS panel. Mail just starts landing in spam, and the DNS change that caused it happened weeks before the first complaint.
Lesser known: the same RFC also caps void lookups, queries that return no answer or NXDOMAIN, at 2. A stale include for a service you cancelled last year can permerror the record even when the total count is fine. Count lookups every time you touch the record, delete includes for tools you no longer use, and replace stable infrastructure with plain ip4 mechanisms, which cost nothing.
DKIM survives what SPF can’t
DKIM takes the opposite approach: instead of listing servers, it signs messages. The sending server holds a private key and adds a DKIM-Signature header covering selected headers plus a hash of the body. The public key sits in DNS at selector._domainkey.yourdomain.com, so receivers can verify. The spec is RFC 6376 from 2011, itself a merger of Yahoo’s DomainKeys and Cisco’s Identified Internet Mail.
Because the signature travels inside the message, DKIM survives forwarding. What breaks it is modification: mailing lists that append footers or rewrite subjects invalidate the body hash. For app transactional mail that rarely matters; for anything flowing through lists, it’s the known failure mode.
Setup details worth knowing before they bite. Keys should be 2048-bit, the recommendation since RFC 8301 (2018) deprecated weaker crypto. A 2048-bit public key is longer than the 255-character limit of a single TXT string, so it has to be split into two quoted strings; good DNS providers do that silently, some panels reject the record instead. And the p= value is a base64-encoded public key, safe to publish because base64 is encoding, not encryption, and this is a public key anyway. Selectors (the s= tag) exist so you can rotate keys: publish a new selector, switch signing over, retire the old one.
DMARC: alignment and policy
DMARC lives at _dmarc.yourdomain.com, minimal form v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com. The spec is RFC 7489 from March 2015, and here’s a detail almost nobody mentions: it’s an informational RFC, never standards-track. The mechanism the world’s inboxes gate on was published as “for information”.
Its core idea is alignment. An SPF pass only helps DMARC if the envelope domain matches the From domain. A DKIM pass only helps if the d= domain in the signature matches the From domain. Relaxed alignment (the default) accepts the same registered domain, strict requires an exact match. One aligned pass is enough, and this is the answer to the most confusing deliverability state there is: SPF pass, DKIM pass, DMARC fail. Your email provider signed with its own domain and used its own bounce domain, both checks passed for the provider, and none of it covered the domain your users see. The fix is almost always custom DKIM signing with your own domain at the provider.
The p= tag tells receivers what to do with failures: none (deliver, but report), quarantine (spam folder), reject (bounce). pct= applies the policy to a percentage while you ramp up, sp= sets a separate policy for subdomains.
The February 2024 rules
In February 2024, Gmail and Yahoo turned long-standing best practices into requirements for bulk senders, defined by Google as roughly 5,000 or more messages to personal Gmail accounts in a 24-hour period. The list: both SPF and DKIM (not either), a DMARC record of at least p=none, an aligned From domain, and a spam complaint rate in Google Postmaster Tools under 0.3%, ideally under 0.1%. One-click unsubscribe per RFC 8058 (the List-Unsubscribe-Post header) became mandatory for promotional mail, with the deadline pushed to June 2024, and unsubscribes have to be honored within two days. Enforcement was phased: from April 2024, Google started rejecting a percentage of non-compliant traffic outright.
It worked, by Google’s own numbers: 265 billion fewer unauthenticated messages reached Gmail in 2024 than in 2023, a 65% drop, and 2.5 million domains published authentication records within the first 60 days. Which also means the bar moved for everyone. Filters now assume authentication; mail without it starts every delivery attempt in a hole.
Below 5,000 a day you escape the DMARC mandate, but not the rest: Google requires SPF or DKIM from all senders, and once you cross the bulk threshold you’re classified as a bulk sender going forward. Setting up all three at low volume takes an hour and spares you doing it during an incident.
DMARC reports, in theory readable
The rua address in your DMARC record starts receiving aggregate reports: gzipped XML attachments, typically one per receiver per day, with a row per source IP showing SPF, DKIM and alignment results. In practice nobody reads them raw. A domain with a handful of senders gets reports from Google, Microsoft, Yahoo and a long tail of smaller receivers, each in its own file.
Realistic options: point rua at a DMARC reporting service that aggregates them, or for a one-off investigation, unzip a report and run it through our XML to JSON converter to get something you can scan for failing IPs. What you’re looking for during monitoring is legitimate senders failing alignment, the invoicing tool someone connected in 2023 being the classic, because those are the ones a p=reject would silently kill.
The order we’d do it in
- DKIM first. Enable custom-domain signing at every service that sends as you. This is the check that aligns and survives forwarding.
- Then SPF. One record, includes merged, lookups counted,
~alluntil you trust it, then-all. - DMARC at
p=nonewith aruaaddress, then let reports accumulate for two to four weeks. - Fix what the reports surface, then move to
p=quarantinewithpct=25, ramp up, and finish atp=reject.
One DNS habit that saves real pain: create the record first, query it after. Checking whether selector._domainkey resolves before you’ve saved it caches the record’s absence at your resolver, and then it exists everywhere except on your machine. That’s negative caching, and it’s covered in our piece on why DNS propagation is a myth, along with why these TXT changes show up faster than the “48 hours” everyone quotes.
Email authentication questions
Why do my emails go to spam even though SPF and DKIM pass?
Usually because of DMARC alignment: SPF and DKIM can both pass for your email provider’s domain while the From header shows your domain, and then DMARC fails anyway. Check the Authentication-Results header (in Gmail: Show original) for spf=pass and dkim=pass and look at which domain each verdict is for. If neither passing domain matches the From domain, fix the DKIM d= domain at your provider first; that is the alignment path that survives forwarding.
What is the difference between SPF, DKIM and DMARC?
SPF is a DNS record listing which servers may send mail for a domain, DKIM is a cryptographic signature proving a message came from a domain and wasn’t altered, and DMARC ties both to the From address the user actually sees and tells receivers what to do on failure. SPF checks the invisible envelope sender, DKIM checks a signature header, and DMARC only passes if at least one of them passes for a domain that matches the visible From domain.
How do I check if SPF, DKIM and DMARC are set up correctly?
Send a message to a Gmail address you own and read the Authentication-Results header via Show original: you want spf=pass, dkim=pass and dmarc=pass in one place. For the DNS side, query the records directly: the SPF TXT record at your domain root, DKIM at selector._domainkey.yourdomain.com, and DMARC at _dmarc.yourdomain.com. One passing test message beats any number of online checkers, because it tests the whole chain including alignment.
What does DMARC p=none actually do?
Nothing to your mail, and that is the point: p=none tells receivers to deliver as usual but send you aggregate reports about every message claiming to be from your domain. It is the monitoring mode you start with, so you can find legitimate senders (the CRM, the helpdesk, that one cron job) that would fail a stricter policy. Since February 2024, p=none is also the minimum Gmail and Yahoo accept from bulk senders, so it went from optional to baseline.
Can I have two SPF records on one domain?
No. RFC 7208 says multiple v=spf1 records must result in permerror, which receivers treat as failed authentication, so two records are worse than one incomplete record. This happens constantly when a second tool’s setup guide says “add this TXT record” and someone does, instead of merging the include into the existing record. Combine all mechanisms into a single v=spf1 record and delete the rest.
Does SPF work when email is forwarded?
No, plain forwarding breaks SPF by design: the forwarding server sends the message onward from its own IP, which isn’t in the original domain’s SPF record, so the check fails on perfectly legitimate mail. Some forwarders rewrite the envelope sender (SRS) to work around it, many don’t. DKIM survives forwarding because the signature travels inside the message, which is exactly why DMARC accepts either an aligned SPF pass or an aligned DKIM pass.
Do I need DMARC if I send fewer than 5,000 emails a day?
Formally, Gmail’s DMARC requirement applies to bulk senders, meaning about 5,000 or more messages to personal Gmail accounts in a 24-hour period. Practically, you want DMARC anyway: all senders need SPF or DKIM as a minimum since 2024, spam filters score authenticated mail better across the board, and a p=none record costs you one TXT entry and gives you reports showing who is sending as your domain. There is no volume below which spoofing stops being your problem.
What is a DMARC report?
An aggregate XML file that receivers like Google email to the address in your rua tag, typically once a day per receiver, listing every source IP that sent mail claiming your domain and whether SPF, DKIM and alignment passed. The format is machine-readable and human-hostile: gzipped XML attachments with one row per IP. Most people feed them into a reporting service; for a one-off look, unzipping and converting to JSON gets you something you can actually scan.