Three layers to prove the email is yours
SMTP was born on an internet of mutual trust and never built in authentication: the server delivering a message simply asserts the sender, and the destination, historically, believed it. Today’s three mechanisms were bolted on top of the protocol to fix that without breaking it. They solve distinct problems and do not replace each other, a receiving provider tends to trust a message that passes all three and to reject or spam-folder one that fails.
| Record | What it proves | Identity it validates | Where it lives in DNS |
|---|---|---|---|
| SPF | That the sending server is authorized | MAIL FROM (envelope, RFC 5321) | TXT on the domain |
| DKIM | That the message was not tampered with | Signing domain (d=) | selector._domainkey.domain |
| DMARC | That SPF or DKIM aligns with the visible “From” | From (header, RFC 5322) | _dmarc.domain |
Look at the middle column: each mechanism authenticates a different identity. That is where the confusion in nine out of ten setups lives. SPF looks at an address the user never sees; DMARC looks at the address they do see. When those two addresses belong to different domains, which happens constantly with sending platforms, SPF can say “pass” while DMARC says “fail”. The rest of this guide is about reconciling those identities.
- Envelope / MAIL FROM (RFC 5321)
- The address declared in the SMTP conversation, where bounces return (Return-Path). It does not appear in the mail client. It is what SPF checks.
- Header From (RFC 5322)
- The “From” field the recipient reads on screen. It is what phishing forges and what DMARC protects.
- Alignment
- The DMARC rule: the domain authenticated by SPF or DKIM must match the From domain. Without a match there is no DMARC pass, even with valid SPF and DKIM.
SPF: who may send for the domain
SPF (Sender Policy Framework, RFC 7208) is a single TXT record that lists, through mechanisms, the servers authorized to use your domain in MAIL FROM. The recipient takes the connecting IP and compares it against that list. If it matches, SPF returns pass; if not, the outcome depends on the qualifier that closes the record.
example.com. IN TXT "v=spf1 include:_spf.google.com ip4:198.51.100.25 -all"
v=spf1 → versão do registro (obrigatória, sempre primeiro)
include:_spf.google.com → autoriza os IPs do Google Workspace (1 consulta de DNS)
ip4:198.51.100.25 → autoriza este IP literal (0 consultas)
-all → hardfail: rejeita qualquer outro servidor| Mechanism | What it authorizes | Counts toward the 10? |
|---|---|---|
| all | Matches everything; closes the record with a qualifier | No |
| ip4 / ip6 | A literal IP or CIDR range | No |
| a | The IPs of the domain’s A/AAAA record | Yes (1) |
| mx | The IPs of the domain’s MX servers | Yes (1) |
| include | Imports another domain’s SPF (the provider) | Yes (1 + those inside) |
| exists | Tests whether a name resolves (uses macros) | Yes (1) |
| ptr | Reverse DNS, discouraged by RFC 7208 | Yes (1) |
| redirect= | Modifier: replaces the record with another domain’s | Yes (1) |
Each mechanism can take a qualifier that decides the verdict when it matches. In practice, the only qualifier that matters is the one on the trailing “all”, because it defines what happens to everyone who is not on the list.
| Qualifier | Result | Practical effect on trailing …all |
|---|---|---|
| + | Pass (default) | +all authorizes ANY server, never use it |
| - | Fail (hardfail) | -all rejects anyone not on the list (recommended) |
| ~ | SoftFail | ~all accepts but flags as suspicious (transition) |
| ? | Neutral | ?all expresses no opinion, almost no protection |
There is a structural trap in SPF that silently knocks out whole domains: the 10-DNS-lookup limit. RFC 7208 (§4.6.4) requires the evaluation to make no more than 10 lookups triggered by include, a, mx, ptr, exists and redirect, counting the nested lookups inside each include. Blowing that ceiling is neither softfail nor neutral: it is permerror, and SPF is treated as if it did not exist. Since every provider you add (Google, a marketing platform, a CRM, an invoicing tool) brings its own include, the budget runs out faster than it looks. It is worth walking the count.
# Exemplo ilustrativo: um SPF com includes aninhados que ESTOURA o limite
example.com: v=spf1 include:_spf.google.com include:spf.plataforma.net \
include:_spf.crm.com -all
_spf.google.com: v=spf1 include:_netblocks.google.com include:_netblocks2.google.com \
include:_netblocks3.google.com ~all
_netblocks*.google.com: só ip4/ip6 (0 consultas cada)
spf.plataforma.net: v=spf1 a mx include:relay.plataforma.net ~all
relay.plataforma.net: só ip4 (0 consultas)
_spf.crm.com: v=spf1 include:eu._spf.crm.com include:us._spf.crm.com ~all
eu/us._spf.crm.com: só ip4 (0 consultas cada)| # | Term that triggers the lookup | Running total |
|---|---|---|
| 1 | include:_spf.google.com | 1 |
| 2 | ↳ include:_netblocks.google.com | 2 |
| 3 | ↳ include:_netblocks2.google.com | 3 |
| 4 | ↳ include:_netblocks3.google.com | 4 |
| 5 | include:spf.plataforma.net | 5 |
| 6 | ↳ a | 6 |
| 7 | ↳ mx | 7 |
| 8 | ↳ include:relay.plataforma.net | 8 |
| 9 | include:_spf.crm.com | 9 |
| 10 | ↳ include:eu._spf.crm.com | 10 |
| 11 | ↳ include:us._spf.crm.com → over the limit | 11 → PermError |
Eleven lookups, one over the cap: the whole record returns permerror and SPF is discarded. Notice how innocent the three providers look, three includes at the top, yet Google alone spends four because of its own internal includes. The way out is to flatten (replace includes with ip4/ip6 ranges when the provider publishes stable IPs), remove sending sources you no longer use, or delegate sending to a dedicated subdomain. The SPF, DKIM and DMARC checker shows the “lookups: n/10” counter and raises a warning from 8 onward. To inspect what each include resolves to, the DNS lookup returns the raw TXT of each name.
DKIM: the signature that proves integrity
DKIM (DomainKeys Identified Mail, RFC 6376, now an Internet Standard, STD 76) solves a problem SPF never touches: integrity. The sending server computes a hash of the body and of a chosen set of headers, and signs that set with a private key. The signature travels inside a DKIM-Signature header; the matching public key is published in DNS. If the subject or the body changes a single byte along the way, the signature stops matching. It is the same family of ideas as the guide on hashing, encryption and encoding: here we use a hash to detect tampering and an asymmetric key to prove authorship.
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com;
s=selector1; h=from:to:subject:date;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=AbCdE...assinatura codificada em base64...==
a= → algoritmo (rsa-sha256; ed25519-sha256 também é padrão)
c= → canonicalização cabeçalho/corpo (relaxed/relaxed)
d= → DOMÍNIO que assina, é ele que o DMARC alinha contra o From
s= → SELETOR: aponta para s=selector1 → selector1._domainkey.example.com
h= → lista de cabeçalhos cobertos pela assinatura
bh= → hash do CORPO (body hash)
b= → a assinatura sobre os cabeçalhos de h= mais o bh=selector1._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq…IDAQAB"
v=DKIM1 → versão do registro
k=rsa → tipo de chave (rsa; ed25519 também é possível)
p=… → a chave PÚBLICA em base64; p= vazio significa seletor REVOGADO- Selector (s=)
- A label you choose that points to a specific key. It lets you keep several active keys and rotate them without downtime: publish a new selector and migrate.
- Canonicalization (c=)
- How the header and body are normalized before hashing. “simple” is strict (any change breaks it); “relaxed” tolerates the whitespace and line-wrapping differences servers introduce in transit. That is why relaxed/relaxed is the most used.
- Body hash (bh=) and signature (b=)
- bh= is the hash of the body alone; b= is the cryptographic signature over the headers listed in h= plus the bh= itself. Verifying both separates “the body changed” from “a header changed”.
DKIM’s great virtue is surviving hops that break SPF. Because the proof travels inside the message rather than being tied to the connection IP, a forward that rewrites the envelope does not destroy the signature, as long as nobody alters the body or the signed headers. That is why, in practice, aligned DKIM tends to be the most reliable path to a DMARC pass.
DMARC: alignment, policy and reports
DMARC is the layer that gives the other two meaning. On their own, SPF and DKIM authenticate domains the user never sees. DMARC (originally RFC 7489, now re-standardized by RFC 9989) does three things: it requires SPF or DKIM to align with the visible From domain, it defines a policy for failures, and it asks for reports back. Alignment is the heart, without it, everything falls apart.
It is worth building the example that confuses everyone: an email that passes SPF and still fails DMARC. It is the typical scenario of anyone sending a newsletter through a marketing platform.
Return-Path: <[email protected]> ← envelope (o SPF olha aqui)
DKIM-Signature: ... d=plataforma-envio.com; s=k1 ← assinatura da plataforma
From: Promoções <[email protected]> ← cabeçalho (o usuário vê isto)
SPF : pass para plataforma-envio.com (o IP está no SPF DA PLATAFORMA)
DKIM : pass para plataforma-envio.com (a assinatura d= confere)
Alinhamento DMARC, comparado com o From = loja.com.br:
SPF alinhado? plataforma-envio.com ≟ loja.com.br → NÃO
DKIM alinhado? plataforma-envio.com ≟ loja.com.br → NÃO
Resultado DMARC: FAIL → com p=reject em loja.com.br, a mensagem é RECUSADAThe fix is not to touch the policy but to reconcile the identities. Two ways out: ask the platform to sign DKIM with d=loja.com.br (a delegated key), which aligns DKIM; or set the Return-Path to a subdomain of yours, like bounce.loja.com.br, which aligns SPF. Only one of the two needs to align for DMARC to pass. It is exactly the rule Google and Yahoo have required of large senders since 2024: SPF and DKIM in place, and at least one of them aligned to the From.
How strict alignment is comes from two tags: aspf (for SPF) and adkim (for DKIM). Each takes “r” (relaxed, the default) or “s” (strict). In relaxed mode, matching the organizational domain is enough, news.loja.com.br aligns with loja.com.br. In strict mode, the domain must be identical, news.loja.com.br does not align with loja.com.br. Always start relaxed.
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:[email protected]; \
adkim=s; aspf=r; pct=100"
v=DMARC1 → versão (obrigatória, sempre primeiro)
p= → política do domínio: none | quarantine | reject
rua= → destino dos relatórios AGREGADOS (diários, em XML)
ruf= → destino dos relatórios de FALHA por mensagem (opcional; cuidado com privacidade)
adkim=s → alinhamento DKIM estrito (domínio idêntico)
aspf=r → alinhamento SPF relaxado (mesmo domínio organizacional)
pct=100 → aplica a política a 100% das mensagens| Policy (p=) | What the recipient does | When to use it |
|---|---|---|
| none | Only observes and reports; nothing is blocked | Start: gather reports at no risk |
| quarantine | Sends non-aligning mail to spam | Middle: once legitimate mail passes |
| reject | Refuses delivery at the SMTP connection | End: maximum protection against spoofing |
Aggregate reports (rua) are what make DMARC operable: they arrive by email, in XML, and list which IPs sent on your domain’s behalf, how many messages, and how each one fared on SPF, DKIM and alignment. In the 2026 revision (the so-called DMARCbis), the standard was split into three documents, RFC 9989 (the protocol), RFC 9990 (aggregate reports) and RFC 9991 (failure reports), and, crucially, DMARC stopped being merely Informational and entered the IETF Standards Track as a Proposed Standard. For operators, the day-to-day does not change: publish, read the reports, tighten.
Why SPF breaks on forwarding, and what ARC and SRS do
SPF’s Achilles’ heel is forwarding. You send an email to someone who redirects their mailbox to another provider; the relaying server uses YOUR domain in MAIL FROM, but its IP is not in your SPF, because how would it be? You never authorized a third party’s server. SPF then fails, even though the message is legitimate. Two distinct solutions attack the problem at different points in the chain, and both are worth knowing before you move to p=reject.
- 2006SPF, experimental (RFC 4408)
The first attempt to authorize servers by IP is published as an experimental standard.
- 2007DKIM (RFC 4871)
The cryptographic signature merges DomainKeys (Yahoo) and Identified Internet Mail (Cisco).
- 2011DKIM becomes an Internet Standard (RFC 6376, STD 76)
The consolidated DKIM reaches the top of the IETF maturity ladder.
- 2014SPF re-standardized (RFC 7208)
SPF sheds its experimental status and becomes a Proposed Standard, with the 10-DNS-lookup limit.
- 2015DMARC (RFC 7489)
Binds SPF and DKIM to the visible From, creates alignment and reports, published as Informational.
- 2019ARC (RFC 8617)
Experimental standard that preserves the authentication result across mailing lists and forwarders.
- 2024Google and Yahoo require authentication
From February 1, senders of more than 5,000 messages/day to Gmail need aligned SPF, DKIM and DMARC.
- 2026DMARCbis (RFC 9989/9990/9991)
Three RFCs obsolete 7489 and move DMARC onto the Standards Track as a Proposed Standard.
Why SPF breaks on forwarding, precisely
SPF checks the connection IP against the MAIL FROM domain. In a simple forward (an automatic mailbox “forward”), the intermediate server resends keeping your domain in the envelope, but the connection now comes from its IP. Since that IP was never in your list, SPF fails. The message is the same, the content legitimate, only the path changed. This is why nobody should rely on SPF alone for the verdict, and why DMARC accepts alignment via SPF OR via DKIM.
SRS, rewriting the envelope so SPF passes again
SRS (Sender Rewriting Scheme) acts at the forwarder: instead of keeping your domain in MAIL FROM, it rewrites the envelope to an address in its own domain, reversibly stashing the original so bounces return to the right place. SPF then checks the domain of whoever actually resends, and passes again. SRS has no formal RFC, it is a 2004 technical specification (Shevek, from Meng Weng Wong’s idea) adopted de facto by servers and providers as a convention. It fixes SPF, but not alignment with the From: so, on its own, it does not save DMARC.
ARC, carrying the authentication verdict across hops
ARC (Authenticated Received Chain, RFC 8617, experimental) tackles it from the other end. Each trusted intermediary, a mailing list, say, that rewrites the subject and breaks DKIM, records, signed, the authentication result IT saw before touching the message. The final destination, if it trusts that chain, can accept an email that would fail “raw” DMARC, because the ARC chain proves it was legitimate before the path’s edits. It is a bridge of trust between servers, not a replacement for SPF or DKIM.
What DMARC does NOT solve: look-alike (cousin) domains
DMARC protects your exact domain. It does nothing against an attacker who registers loja-pagamentos.com or l0ja.com.br and sends perfectly authenticated email from there, after all, it is THEIR domain, with their own SPF and DKIM. To the human eye it looks like you; to DMARC it is a different domain, and it passes. Defenses against this live outside the trio: monitoring for look-alike registrations, reputation- and content-based anti-phishing filters, and programs like BIMI, which shows your verified logo next to messages (requiring DMARC at quarantine with pct=100 or reject) to give the user a visual authenticity cue. A WHOIS of the suspicious domain helps you see when it was registered and by whom.
How to roll it out without blocking legitimate mail
- Inventory and publish SPF and DKIMList EVERY sending source (Google/Microsoft, marketing, CRM, invoicing, forms) and cover each in SPF without blowing the 10 lookups; ask each provider for DKIM with d= aligned to your domain.
- Put DMARC at p=none with ruaPublish _dmarc with p=none and rua pointing to a mailbox of yours. Nothing is blocked; you are only turning on the report stream.
- Read the reports for weeksIn the aggregate XML, look for legitimate sources failing alignment and fix each one (delegated DKIM or a Return-Path on a subdomain of yours).
- Tighten to p=quarantineOnce legitimate mail passes, move to quarantine (optionally with a lower pct at first) and watch whether anything lands in spam unfairly.
- Close at p=rejectWith everything stable, go to reject: from then on, any message that does not align with your From is refused at the door.
It helps to know that authenticating the sender is one piece of a larger ecosystem. MTA-STS (RFC 8461) and TLS-RPT (RFC 8460) are about forcing and reporting TLS on server-to-server delivery; BIMI handles the verified logo. None of these replace the trio, they all assume DMARC is working. To read the authentication verdict of a message that already arrived (the Authentication-Results header, with that email’s SPF, DKIM and DMARC results), use the email header analyzer; it rebuilds the route and shows where authentication passed or failed.
- A single SPF record, ending in -all (or ~all during transition), within the 10 DNS lookups.
- DKIM active with a key of at least 2048 bits and d= aligned to the From domain.
- DMARC published at _dmarc, with rua to a mailbox someone actually reads.
- At least one identity, SPF OR DKIM, aligned with the From on every sending source.
- Progression none → quarantine → reject only after the reports confirm legitimate mail passes.
Frequently asked questions
Why does my email pass SPF but fail DMARC?
Do I need all three or is SPF enough?
What is the SPF 10-DNS-lookup limit?
Why start with p=none?
DKIM or SPF: which survives forwarding?
Does DMARC protect against domains that look like mine?
Has DMARC become an official standard?
SPF authorizes servers by the envelope, DKIM signs the content, and DMARC ties both to the visible From by requiring alignment, that is why an email can pass SPF and still fail DMARC. Stay within SPF’s 10 lookups, make sure at least one identity aligns per sending source, understand forwarding (which SRS and ARC try to fix), and roll out in order: p=none with rua, read the reports, and only then quarantine and reject.
Sources & references
- RFC 7208, Sender Policy Framework (SPF)
- RFC 6376, DomainKeys Identified Mail (DKIM), STD 76
- RFC 7489, DMARC (original, Informational, now obsoleted)
- RFC 9989, DMARC (DMARCbis, obsoletes RFC 7489)
- RFC 8617, Authenticated Received Chain (ARC)
- RFC 8461, SMTP MTA Strict Transport Security (MTA-STS)
- Google, Email sender guidelines