Guide
SPF, DKIM and DMARC: a practical website-owner checklist
Derived from the specifications rather than from anyone else's measurements, so the advice holds the same whether your mail sits on a shared host, a dedicated address, or a platform that signs on your behalf.
If somebody can send email as you, your domain's reputation is theirs to spend. Not always in the obvious direction — the useful attacks are the ones that look like real mail from you, arrive at a colleague, and ask for something. The three records below are what make that difficult, and between them they are the whole of the modern answer to email spoofing.
They are also the layer of external security you can check without sending a single request to your website, which is why this guide and the DNS and email checker both work from published records alone.
The problem these three records solve
Email has no built-in authentication. The From header is just a
string the sending server wrote, and nothing about SMTP lets a receiver verify
who the sender claimed to be. So a receiver that wants to check has nothing to
check — unless the domain publishes something that lets it.
Each record answers a different part of one question, did this message really come from the domain it claims?:
- SPF — is this server allowed to send as you?
- A list, in DNS, of the hosts permitted to send mail for your domain. A receiver evaluates it against the sending server's IP address.
- DKIM — has the message been altered since it was signed?
- A private key signs the outgoing message; the public half is published in DNS. A receiver that verifies the signature knows the message came from the key holder and was not modified afterwards.
- DMARC — what should the receiver do when they disagree?
- A policy saying what to do with a message that fails both. This is the record that makes the other two consequential rather than advisory, and it is the one most often left in a mode that only watches.
The order matters and is often got wrong. SPF alone is defeated by forwarding, because forwarding breaks the IP check while the signature — if there is one — survives. DKIM alone means anybody can sign as you if they can find or guess a selector. DMARC without either of the other two is a policy with nothing to evaluate. The three work as a set.
SPF: which servers may send as you
A TXT record at your apex domain, starting with v=spf1. A
minimal real-world example:
v=spf1 include:_spf.example-mailprovider.com -all
Reading it left to right:
v=spf1— version marker, required.include:— delegate to another domain's SPF record. This is how you authorise a mail provider without copying their address list.-all— hard fail. Everything not matched above is not authorised.
The qualifiers on the all mechanism are the single most
consequential thing on this record:
| Ending | What it means | Use it when |
|---|---|---|
-all |
Fail. Servers not listed should not send as you. | You have enumerated every legitimate sender. This is the ending you want. |
~all |
Soft fail. Not authorised, but treat it as a hint rather than a failure. | You are not yet sure you have found every sender. Treat as a migration state, not a destination. |
+all |
Pass. Any server may send as you. | Never. This authorises the entire internet to impersonate your domain, and is equivalent to having no SPF record at all. |
?all |
Neutral. Neither pass nor fail. | Rarely. It silences a check without asserting anything. |
A record ending in +all is a genuine finding rather than a
cosmetic one: it is the SPF configuration that makes the domain trivially
spoofable, and it is usually left there by accident during setup.
Two more SPF constraints worth knowing before you edit the record:
- Ten DNS lookups. Each
include:,a:,mx:,ptr:andexists:costs one. Exceed ten and the check fails with a permerror, which is treated as no SPF pass at all. Providers nest includes, so this is reached by accumulation rather than by a single large list. - SPF is evaluated against the envelope sender — the
MAIL FROMaddress, which a person never sees — not against the visibleFrom. This is the detail that surprises people and it is the reason alignment, below, exists.
DKIM: proving the message was not changed
DKIM signs selected headers and the body with a private key held by the sending server. The public key is published in DNS, but not at your apex — at a selector path beneath it.
selector1._domainkey.example.com TXT "v=DKIM1; k=rsa; p=MIIBIjANBg…"
The p= value is the public key, base64-encoded. The selector
name is chosen by whoever generates the key, which is why you often need to
know it: providers publish theirs in their setup instructions, and a DKIM
check that does not find the selector it expects reports nothing at all rather
than a failure.
Two operational points:
- Key size. 2048 bits is the practical floor. Some receivers will not verify a 1024-bit key, which means your mail silently loses one of its two signals.
- Revocation. Publishing the selector with an empty
p=tag —"v=DKIM1; k=rsa; p="— tells every receiver to stop trusting that key. A revoked key is a good thing to find and an easy thing to mistake for a missing one.
Rotate DKIM keys by publishing a second selector with the new key, switching senders to it, waiting for mail in flight to age out, then revoking the old selector. Revoking first breaks signature verification on every message still being forwarded or held.
DMARC: deciding what to do when they fail
A TXT record at _dmarc.example.com. The minimum useful form:
v=DMARC1; p=none; rua=mailto:[email protected]
The parts:
v=DMARC1— version.p=— the policy.noneobserve and report,quarantineask receivers to treat failing mail as spam,rejectask them to refuse it outright.rua=— where aggregate reports go. You cannot act on a policy you get no data about, so this is not optional in practice.adkim=andaspf=— alignment mode;sfor strict (the default) orrfor relaxed.pct=— what percentage of messages the policy applies to. Useful for a staged rollout, and worth removing afterwards.
The policy is the enforcement. p=none collects data
and asks for nothing; a large number of domains sit there indefinitely having
gathered reports nobody reads. The value of the rua address is
that it tells you which senders are failing, which is the information you need
to move the policy forward.
Alignment, which is the part that decides everything
DMARC passes a message when at least one of SPF or DKIM
passes and aligns. Alignment is a separate test: the identifier
checked must match the domain in the visible From header.
So consider a marketing platform sending on your behalf:
From: [email protected]
Return-Path: [email protected]
DKIM-Signature: d=example-platform.com; …
- SPF passes —
example-platform.comis authorised in your SPF record. But it does not align:example-platform.com≠example.com. No SPF credit. - DKIM passes — but with
d=example-platform.com, which also does not align. No DKIM credit. - DMARC fails. Neither aligned check passed.
Both checks passing is worth nothing without alignment. The fix is on the
provider's side — they must sign with a key at example.com, which
is why setting up DKIM for a sending platform is not optional. Under
adkim=r the requirement relaxes to a shared organisational
domain, which is looser and which example-platform.com would
still fail — the two domains here share no registrable domain.
This is the reason a site with a correct-looking SPF record and a working DKIM integration can still fail DMARC: it usually means the DKIM key was generated by a service that never had the ability to sign as your domain.
A rollout order that does not break mail
Doing this in the wrong order sends your own mail to spam, which is how a lot of DMARC projects quietly get abandoned. The order below is the one that works:
- Inventory what actually sends. Check the
Fromaddresses on mail your organisation sends — transactional mail from your application, your CRM, your support desk, your password resets, your monitoring. Each one needs an SPF authorisation and a DKIM signature. - Publish SPF, ending in
~all. Soft fail. Nothing breaks and nothing is enforced, but from now on every send is being evaluated. - Publish DKIM for every sender in the inventory. One at a
time, verifying each is signing. Watch the outbound headers of real messages
—
Return-PathandDKIM-Signature— rather than assuming. - Publish DMARC at
p=nonewith anruaaddress you will actually read. Nothing is enforced. This step exists to collect the data for the next one. - Read the reports. This is the step everybody skips and it is the only one that tells you what the next step should be. Every legitimate sender that fails is a support ticket you can prevent.
- Move to
quarantine, or straight torejectif the reports are clean. - Tighten SPF. Change
~allto-allnow that the enumeration is trustworthy. An SPF record left at soft fail is a permanent half-measure.
Every step except the last two is reversible by changing a DNS record,
which takes minutes to propagate and no deploy. That is the argument for doing
it this way rather than switching on reject on a Friday.
Checking your own records
To read them by hand, look up the four records:
dig +short TXT example.com # SPF
dig +short TXT _dmarc.example.com # DMARC
dig +short MX example.com # who you actually send through
dig +short TXT selector1._domainkey.example.com # DKIM, if you know the selector
The selector is the hard part by hand, which is why the DNS and email checker reports all of this — presence, policy strength, lookup-count risk, whether the selector is publishing a key at a usable size — from the same published records, with no request sent to your site at all.
What it cannot do is check that your providers are actually signing. SPF and
DKIM are evaluated by the receiving server at delivery time, so the definitive
test is the DKIM-Signature and Return-Path headers
on a real message you sent yourself.