← Back to blog

Email Deliverability in Plain English: SPF, DKIM, and DMARC

7 min read
emaildeliverabilityguide

Your password reset email works in testing, works in staging, and then a customer says they never got it. You check the logs: delivered. You check their spam folder over a screen share: there it is, between a fake invoice and a crypto newsletter.

Nine times out of ten, the culprit is not your email copy or your provider. It is one of three DNS records — SPF, DKIM, or DMARC — that is missing, wrong, or misaligned. The specs for these are famously dense, but the ideas behind them are simple. Here they are in plain English, with real examples and the commands to check your own domain.

One analogy that actually holds

Imagine your company sends physical mail, and the postal service is deeply suspicious of forgery.

  • SPF is the list of approved couriers. You file a public notice: "Mail from our company is only delivered by these carriers." If a letter claiming to be from you arrives via a courier not on the list, the recipient gets suspicious.
  • DKIM is the tamper-evident seal. Every letter you send is stamped with a seal only you can produce. The recipient checks the seal against your published pattern. If the letter was altered in transit — or the seal is missing — the check fails.
  • DMARC is the policy letter. It tells recipients what to do when the courier check or the seal check fails: "deliver it anyway," "put it in the suspicious pile," or "refuse it" — and "mail me a weekly report of everything you saw."

The three work together. SPF and DKIM each answer "is this really from them?" in a different way. DMARC decides what happens when the answer is no, and gives you visibility either way.

The records, for real

All three are TXT records in your DNS. Here is what they actually look like.

SPF lives at the root of your sending domain and lists who may send on its behalf:

example.com.  TXT  "v=spf1 include:_spf.google.com include:sendgrid.net ~all"

The include: entries pull in your providers' approved server lists. The ~all at the end means "anything else is a soft fail" — treat it with suspicion. -all is the strict version: reject outright.

DKIM lives at a selector under _domainkey and publishes the public half of a signing key:

s1._domainkey.example.com.  TXT  "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEB..."

Your provider signs each outgoing message with the private half; receivers fetch this record to verify the signature. The selector (s1 here) is chosen by the provider — you will usually be given one or two CNAME or TXT records to add during domain setup.

DMARC lives at _dmarc and states your policy:

_dmarc.example.com.  TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

p= is the policy: none (just report), quarantine (spam-folder it), or reject (bounce it). rua= is where aggregate reports get sent — a daily XML digest from each major receiver showing which servers sent mail as your domain and whether it passed.

SPF, DKIM, and DMARC: three seals on every envelope.

Check yours in thirty seconds

You do not need a testing service to see where you stand. dig (or nslookup on Windows) will do:

# SPF
dig TXT example.com +short

# DKIM (use your provider's selector)
dig TXT s1._domainkey.example.com +short

# DMARC
dig TXT _dmarc.example.com +short

If the SPF query returns nothing, or the DMARC query returns nothing, you have found your problem. For a live end-to-end check, send a message to a Gmail address you control, open it, and choose "Show original" — Gmail displays SPF, DKIM, and DMARC as explicit PASS/FAIL lines.

Alignment, explained simply

This is the part most guides mumble through, and it is where most "everything passes but mail still fails DMARC" mysteries live.

SPF and DKIM each involve a domain — but not necessarily your domain. SPF is checked against the invisible bounce address (the Return-Path), which is often your provider's domain, like bounces.sendgrid.net. DKIM is checked against whatever domain signed the message (d= in the signature), which by default may also be the provider's.

DMARC adds one extra requirement: at least one of the passing checks must involve the domain the human actually sees in the From line. That is alignment. SPF can pass for sendgrid.net all day long — if your From address is support@example.com, DMARC shrugs and fails the message anyway.

The fix is the "custom domain" or "domain authentication" step every serious provider offers: it makes DKIM sign as example.com (and usually points the Return-Path at a subdomain of yours). Do not skip that step. It is the single highest-leverage deliverability task on this page.

The five mistakes that land transactional mail in spam

1. Sending from a domain you never authenticated. Using a provider's shared or default sending identity means your From domain has no skin in the game — no aligned DKIM, no aligned SPF. Complete the provider's domain verification before sending anything a customer needs to receive.

2. Two SPF records, or one with too many lookups. A domain must have exactly one SPF record; two means both are ignored. And SPF caps DNS lookups at ten — stack enough include: entries (Google, a CRM, a marketing tool, a helpdesk) and you silently exceed it, which counts as a permanent error. Merge into one record and prune tools you no longer use.

3. DKIM signing as the provider, not you. Everything shows "DKIM pass" in the headers, but the signature's d= is the provider's domain — so it does not align, and DMARC fails. See the alignment section above; this is that bug.

4. Jumping straight to p=reject. A strict policy on day one will happily reject your own legitimate mail from a forgotten source — the billing system, the status page, a cron job on an old server. Start with p=none plus a rua= address, read the reports for two or three weeks, fix what fails, then tighten to quarantine and eventually reject.

5. Mixing marketing blasts with transactional mail on the same domain. Reputation is tracked per sending domain and IP. One aggressive newsletter campaign with high spam complaints drags down the password resets riding the same identity. Put bulk mail on a subdomain (news.example.com) so its reputation is quarantined from the mail that must arrive.

One operational note that makes all of this easier: keep transactional sending going through a single path per app, so there is exactly one Return-Path and one DKIM identity to reason about. This is one of the quiet arguments for routing email through a gateway — in Helmdesk, for instance, each project sends through one configured provider with delivery logs and automatic bounce suppression in one place, so when a message does fail, the evidence is not scattered across three dashboards.

The bottom line

SPF says who may send. DKIM proves the message was not forged or altered. DMARC says what to do about failures — and, crucially, mails you the evidence. Set all three, make sure at least one passing check aligns with your From domain, ramp DMARC from none to reject deliberately, and keep bulk mail off your transactional domain.

None of this is glamorous work. But it is a one-afternoon setup that decides whether the most important emails your app sends — resets, receipts, verifications — actually reach a human.

Transactional email with a paper trail

Send through your own provider, track every delivery and bounce, and keep suppressions automatic — from the same dashboard as your helpdesk.