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:

SPF all qualifiers and what each tells a receiving server
EndingWhat it meansUse 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: and exists: 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 FROM address, which a person never sees — not against the visible From. 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. none observe and report, quarantine ask receivers to treat failing mail as spam, reject ask 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= and aspf= — alignment mode; s for strict (the default) or r for 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.com is 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:

  1. Inventory what actually sends. Check the From addresses 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.
  2. Publish SPF, ending in ~all. Soft fail. Nothing breaks and nothing is enforced, but from now on every send is being evaluated.
  3. Publish DKIM for every sender in the inventory. One at a time, verifying each is signing. Watch the outbound headers of real messages — Return-Path and DKIM-Signature — rather than assuming.
  4. Publish DMARC at p=none with an rua address you will actually read. Nothing is enforced. This step exists to collect the data for the next one.
  5. 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.
  6. Move to quarantine, or straight to reject if the reports are clean.
  7. Tighten SPF. Change ~all to -all now 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.