Email Deliverability Basics for Small Sites: SPF, DKIM, DMARC

Email Deliverability Basics for Small Sites: SPF, DKIM, DMARC

Written by

in

Three DNS records decide whether your email reaches the inbox: SPF lists the servers allowed to send as your domain, DKIM signs every message so the receiver can tell it was not altered on the way, and DMARC tells the receiver what to do when either check fails. Get one wrong and small sites do not see an error — invoices, password resets and replies just land in spam or vanish. Here is the whole setup, in the order I do it.

The short version

  • SPF is one TXT record per domain listing the servers allowed to send. Two SPF records is not redundancy, it is a broken domain.
  • DKIM needs a private key on the mail server and a public key in DNS at selector._domainkey.yourdomain. A DNS row without a signing server is decoration.
  • DMARC is a TXT record at _dmarc that ties the From address to SPF and DKIM and sets a policy. Start at p=none, read the reports, then tighten.
  • Test by sending to a real Gmail address and reading the headers, not a checker that only reads DNS.
  • If WordPress sends through PHP mail(), fix that before touching DNS. Hosting IPs are the ones receivers distrust.

What each record actually does

They fail separately. SPF reads the envelope sender — the Return-Path, which your customer never sees. DKIM reads a cryptographic signature over the headers and body. DMARC reads the From header your customer does see, and asks whether SPF or DKIM passed for that domain.

Record Lives at What it proves Usual mistake
SPF (TXT) @ Which servers may send as you Two SPF records, or over ten lookups
DKIM (TXT) selector._domainkey The message was signed by your key Key in DNS, server never signs
DMARC (TXT) _dmarc What to do on failure, and where reports go p=reject before anything was verified

Where the records go, and what is live now

The records belong in the DNS zone, where the A record lives. In cPanel that is Zone Editor; in Cloudflare it is DNS → Records → Add record, type TXT, TTL Auto. If your nameservers sit at the registrar while the site sits on hosting, the mail records still go at the registrar: email DNS follows the nameservers, not the web server. On Cloudflare, mail records must be DNS only, never proxied — the same rule as in the Cloudflare setup guide.

Look at what is published before you change anything:

# SPF
dig +short TXT example.com | grep -i spf

# DKIM — use the selector your provider gives you
dig +short TXT default._domainkey.example.com

# DMARC
dig +short TXT _dmarc.example.com

# Windows, no dig:
nslookup -type=TXT example.com

Two lines of v=spf1 means the record exists twice — worse than missing.

SPF: one record, ten lookups, and the ending that matters

An SPF record lists who may send, plus a qualifier for everyone else. For a site sending from its own hosting and one outside provider:

v=spf1 a mx include:_spf.provider.example ip4:203.0.113.10 -all

Read it left to right: a allows the server behind your A record, mx your incoming mail hosts, each include: another domain’s SPF list, ip4: a single address. Replace the example IP with yours.

Two rules break more SPF records than anything else. First: one SPF record per domain name. If your host adds one and a marketing tool tells you to add another, merge them into a single line. Second: the ten-lookup limit in the SPF specification. Every include, a, mx, ptr and exists counts toward it, and going over returns a permanent error that receivers read as “no valid SPF”. MXToolbox and dmarcian both report the lookup count; check it before adding another include.

End with -all once you are confident, ~all while you are still finding senders you forgot. Soft fail is a test mode; it protects nothing.

DKIM: the key has to exist on the server

DKIM is the record people copy correctly and still get wrong, because the DNS entry is only half of it. The public key goes in DNS; the private key stays on the machine that sends your mail, and that machine must be configured to sign. Publish the record without enabling signing and every message fails DKIM while you edit DNS for nothing.

The right order: enable DKIM in your hosting panel first — in cPanel it is Email Deliverability — then copy the exact record it generates. Do not generate your own key pair and hope the panel picks it up. The selector is part of the record name and your provider chooses it; default, mail and google are common.

If your DNS host splits a long TXT value into quoted chunks, that is normal; resolvers join them again. Breaking the value yourself is not.

DMARC: monitor first, enforce later

DMARC is the policy layer. Start with this:

v=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=r; aspf=r; pct=100

p=none means “deliver as normal, just tell me what happened”. It protects nothing by itself, and that is exactly why it is the right first step: your reports will show every legitimate sender you forgot, including the contact form on your own site. Point rua at a mailbox you actually read, or at a reporting service — the reports arrive as XML and nobody reads them by hand.

After a few weeks of clean reports, move to p=quarantine and then p=reject, in steps with pct if you send volume. Alignment matters more than it looks: DMARC passes only if SPF or DKIM passes and the domain in that result matches the From domain. A host that sends as yourdomain.com but authenticates as its own domain passes SPF and still fails DMARC — the most common cause of “all my records are green but DMARC fails”.

Test from a real mailbox, then fix the sending path

Send a message from the site’s contact form and one from webmail to a Gmail address you own. Open it, use the three-dot menu, choose Show original, and read the authentication results near the top. You want three passes: SPF, DKIM with your selector, DMARC. Send the same message to an Outlook address too — Microsoft’s filters weigh reputation differently and will catch a problem Gmail forgives.

If SPF passes and DKIM fails, the server is not signing or the selector does not match. If both pass and the mail still lands in spam, the problem is reputation, not authentication: a domain that has never sent mail looks exactly like a spammer’s first domain. Send consistently, keep volume modest at the start, and make sure every message is one somebody asked for.

The WordPress part trips up more small sites than DNS does. PHP mail() sends from the web server’s IP, shared with every other site on that host — and that is the reputation your mail inherits. Configure SMTP to your real mailbox or a transactional provider (WP Mail SMTP and Post SMTP both do it), and check that outbound port 25 is not blocked, which many VPS providers do by default; port 587 exists for that reason. If the host’s IP reputation is the problem, no DNS record saves you, which is worth weighing when you compare cheap hosting against good hosting.

Forwarding is the last trap: a forwarder re-sends with a new envelope sender, so SPF fails at the destination while DKIM usually survives. And when you change hosts, do not touch MX and SPF in the same hour — the migration checklist covers the sequencing so mail does not bounce.

The checklist before I call it done

  • Exactly one SPF record, ending in -all, under ten lookups, covering every tool that sends for you.
  • DKIM signing enabled on the server, selector in DNS matching the signature, confirmed by a real message.
  • DMARC at _dmarc with p=none and a rua mailbox someone reads.
  • Test messages to Gmail and Outlook, all three checks passing in the headers.
  • WordPress sending over SMTP on port 587, not PHP mail().
  • Reverse DNS set for the sending IP if you run your own server.

Do it in that order today: SPF, DKIM, DMARC at p=none, then one test message to your own Gmail and read the headers. It tells you more than any dashboard, because it measures what your customer receives.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *