SPF, DKIM and DMARC, explained for people who run websites

What the three e-mail authentication records do, how they fit together, and the exact records to publish so that nobody can send mail as your domain.

9 min read

E-mail was designed in a world where everyone trusted everyone. The From address of a message is a line of text that the sender types; nothing in the original protocol checks it. Three DNS records, added over the last twenty years, let a domain owner say who is allowed to send in their name and what receivers should do with everything else. Together they are the difference between “anyone can e-mail your customers as you” and “nobody can”.

Most domains that fail a Perimetry audit fail here first, and the fix costs nothing but ten minutes in a DNS control panel.

The three records in one paragraph each

SPF (Sender Policy Framework) is a TXT record on your domain that lists the servers allowed to send mail for it: your mail provider, your newsletter tool, your website’s transactional mail service. A receiving server looks at the address that connected to it, checks it against the list, and gets a result: pass, fail, soft fail or neutral.

DKIM (DomainKeys Identified Mail) is a signature. Your sending server signs each outgoing message with a private key; the matching public key is published in DNS under a selector, for example selector1._domainkey.yourdomain.com. The receiver fetches the key and verifies that the message was not altered and really came from a server that holds your key.

DMARC (Domain-based Message Authentication, Reporting and Conformance) is the policy that ties the other two together. It says: “if a message claims to be from my domain and passes neither SPF nor DKIM in a way that matches the From address, then do this with it: nothing, quarantine it, or reject it.” It also gives receivers an address to send daily reports to, so you can see who is sending as you.

Without DMARC, SPF and DKIM are advisory. A receiver that sees an SPF failure on a message with no DMARC policy will usually deliver it anyway, perhaps with a slightly higher spam score. With a DMARC policy of p=reject, the same message is dropped.

What goes wrong, in the order we see it

No DMARC record at all. This is the single most common finding on small-business domains. The domain may have perfect SPF and DKIM, and still anyone can spoof it, because no policy tells receivers to act on the results.

DMARC with p=none. This is the right place to start, because it gives you reports without affecting delivery. It is the wrong place to stay. A p=none policy protects nothing; it only observes. Many domains were set to none years ago by a well-meaning administrator and never moved.

SPF ending in ?all or ~all when it should end in -all. The last mechanism of an SPF record says what to do with senders not on the list. -all means fail. ~all means soft fail, which most receivers treat as “deliver, but note it”. ?all means neutral: “I have no opinion”, which is the same as having no SPF at all. With DMARC in place, ~all is acceptable because DMARC makes the final call; without it, only -all carries weight.

SPF with more than 10 DNS lookups. Every include:, a, mx, redirect= and exists: in an SPF record costs a DNS lookup, and the receiver gives up after ten. The record then returns a permanent error, which DMARC treats as a failure. This happens quietly when a company adds a fourth or fifth sending service; each provider’s include may itself contain several includes. Perimetry counts the lookups recursively and reports the total.

A dead include. Your SPF still lists a provider you stopped using three years ago. If that provider’s record disappears, your record returns an error. If the provider is acquired or recycles its address space, you have authorised a stranger.

Missing DKIM. Many mail providers enable DKIM only when you ask, by adding two or three CNAME records they give you. Without it, forwarding breaks SPF (the forwarder’s server is not on your list) and the message fails DMARC.

The records to publish

Replace the names in angle brackets. Every line is a separate DNS record.

SPF

One TXT record on the apex domain, listing only the services that actually send for you:

yourdomain.com.  TXT  "v=spf1 include:_spf.google.com include:sendgrid.net -all"

Rules of thumb: one SPF record per domain, never two. Remove every service you no longer use. Keep the total under ten lookups; the Perimetry report shows the count. If you cannot get under ten, ask providers for their IP ranges and use ip4: mechanisms, which are free.

The free SPF record generator writes this record from the services you tick and counts its lookups; the SPF record checker shows what a published record costs, include by include, and explains errors such as permerror: too many DNS lookups.

DKIM

Your mail provider gives you the records. They usually look like this:

selector1._domainkey.yourdomain.com.  CNAME  selector1-yourdomain-com._domainkey.provider.net.
selector2._domainkey.yourdomain.com.  CNAME  selector2-yourdomain-com._domainkey.provider.net.

Then turn on signing in the provider’s console. Do the same for every service that sends in your name: newsletters, invoicing, support desk. Each will have its own selector. Keys should be 2048 bits; Perimetry estimates the key length from the published record and flags 1024-bit keys.

To find the selector a provider uses, look for s= in the DKIM-Signature header of a message you sent, then check the key with the free DKIM checker.

DMARC

Start in monitoring mode:

_dmarc.yourdomain.com.  TXT  "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com; fo=1"

Read the aggregate reports for two to four weeks. Every legitimate sender should appear as passing. Fix the ones that fail (usually a forgotten service that needs DKIM). Then move to quarantine, then to reject:

_dmarc.yourdomain.com.  TXT  "v=DMARC1; p=quarantine; pct=100; rua=mailto:dmarc@yourdomain.com; sp=reject; adkim=s; aspf=r"
_dmarc.yourdomain.com.  TXT  "v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com; sp=reject; adkim=s; aspf=s"

sp=reject applies the policy to subdomains, which attackers favour because nobody watches them. The alignment tags adkim and aspf control how strictly the signing or SPF domain must match the From domain; strict is better once everything works.

The free DMARC record generator builds these records setting by setting, and the DMARC checker reads a published one tag by tag, including whether an external report address has authorised your reports.

Domains that never send mail

A parked domain, a brand variant, a product domain used only for a website: these send nothing, so they can refuse everything. Publish all three records in their strictest form and add a null MX, which tells the world the domain accepts no mail either:

brand-variant.com.         MX   0 .
brand-variant.com.         TXT  "v=spf1 -all"
_dmarc.brand-variant.com.  TXT  "v=DMARC1; p=reject; sp=reject"
*._domainkey.brand-variant.com.  TXT  "v=DKIM1; p="

Perimetry checks for exactly this configuration on domains with no MX records and reports when a non-sending domain is left open.

Two optional records worth adding

MTA-STS tells sending servers that your mail servers support TLS and that they should refuse to deliver over an unencrypted connection. It is a DNS record plus a small policy file served over HTTPS at https://mta-sts.yourdomain.com/.well-known/mta-sts.txt.

TLS-RPT gives senders an address to report TLS failures to:

_smtp._tls.yourdomain.com.  TXT  "v=TLSRPTv1; rua=mailto:tls-reports@yourdomain.com"

Neither affects spoofing, but both are cheap and both appear in a Perimetry report as low-severity findings when absent.

How Perimetry reads these records

The audit resolves the SPF record, follows every include and redirect recursively, counts lookups, and reports syntax errors, dead includes and loops. It probes more than fifty common DKIM selectors (the ones used by Google, Microsoft, Mailchimp, SendGrid, Postmark, Brevo, Amazon SES and others) and estimates the key length of each one it finds. It parses the DMARC record tag by tag, checks that reporting addresses on other domains have the required authorisation record, and compares the policy with the domain’s actual mail setup. The report ends with the exact records to publish for your situation, not a generic template.