Email Authentication with SPF, DKIM, and DMARC
Email authentication helps receiving servers tell whether a message that claims to be from your domain was sent in a way you authorized. It does not stop all spam, and it is not a substitute for consent. It does reduce the chance that a lookalike uses your domain casually, and it helps legitimate mail from your shop or office land in inboxes. This guide explains SPF, DKIM, and DMARC with example records, the order to set them up, SPF’s lookup limit, and how to read reports while you move toward quarantine or reject. Examples use example.com. This is general technical education, not a promise about deliverability.
The three checks in plain language
SPF (Sender Policy Framework) is a DNS TXT record that lists which hosts may send mail for the domain in the “envelope” sender (the bounce address). A receiver looks up SPF and compares the connecting IP.
DKIM (DomainKeys Identified Mail) is a cryptographic signature added by the sending server. The public key lives in DNS. If the signature verifies, the signed parts of the message were not altered in those fields since signing.
DMARC (Domain-based Message Authentication, Reporting, and Conformance) ties the visible From domain to SPF and/or DKIM alignment, then tells receivers what to do on failure (none, quarantine, reject) and where to send XML reports.
You need DNS access. If your mail is sent by several systems (office suite, shop receipts, a newsletter tool), each must be represented in SPF and/or DKIM or DMARC will eventually treat them as failures. How you add hostnames is the same class of work as other records in DNS records explained.
SPF: one policy, many includes
example.com. 300 IN TXT "v=spf1 include:_spf.example.net include:send.example.org -all"
Rules:
- Start with
v=spf1. include:pulls another domain’s SPF (common for mail vendors).ip4:192.0.2.10authorizes a documentation-style IPv4 address you actually send from (use your real IP, not 192.0.2.10 on the public internet).-allmeans fail if no match;~allis “soft fail.” DMARC is happier when SPF is accurate, not when you leave+all(allow the world).
There must be only one SPF TXT record on the name you use (usually the apex). Two SPF records are invalid. Merge includes into a single string. Do not create one SPF per vendor as separate TXT records.
SPF does not look at the visible From: header by itself. That is why DMARC alignment exists.
The 10-lookup SPF limit
When a receiver evaluates SPF, each include, a, mx, and similar mechanism can trigger further DNS lookups. The total is limited to 10. Nested includes count. Exceed the limit and SPF can fail even if your IP is listed somewhere deep in the tree.
If you add every vendor “just in case,” you will hit 10. Flattening (replacing includes with ip4/ip6) is sometimes offered as a product; it can go stale when a vendor changes IPs. Better: remove senders you no longer use, and do not include a vendor you have not configured to send yet.
Check the lookup count after each change with a checker you trust, or by reading the nested records by hand for a small setup.
DKIM: a selector and a public key
Your mail system signs messages and tells you to publish a TXT (or CNAME) at a selector name:
s1._domainkey.example.com. 300 IN TXT ( "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A…" )
The p= value is the public key the vendor gives you (the snippet above is truncated for illustration, not a real key). The selector s1 is arbitrary; vendors pick one. You can have several selectors for several systems.
Some vendors ask for a CNAME from s1._domainkey.example.com to a hostname they control, so they can rotate keys without another DNS ticket. That is fine. Do not invent a key. Do not publish a private key.
After DNS is live, send a test to an inbox that shows authentication results (many show “DKIM: pass”). If it fails, wait for TTL, then confirm the selector name matches what the message’s DKIM-Signature header declares.
DMARC: start at none
_dmarc.example.com. 300 IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; adkim=r; aspf=r"
p=none means “do not punish, just report.” rua is where aggregate XML reports should go. Use a mailbox you will actually export from, or a service that parses DMARC XML. Volume can be noisy. adkim=r and aspf=r are relaxed alignment (default): the organizational domain must match, not always the exact subdomain.
Optional ruf (forensic) reports are more sensitive; many operators skip them. pct= can apply policy to a percentage of failing mail while you roll out.
Publish DMARC only after SPF and DKIM are passing for the mail you care about. Otherwise reports will be a wall of failure you already knew about.
Order of setup
- Inventory every system that sends as @example.com (or a subdomain).
- Set SPF as a single record covering those systems; stay under 10 lookups.
- Enable DKIM for each system and publish selectors.
- Send tests from each system; confirm SPF and DKIM pass.
- Publish DMARC at
p=nonewith anruaaddress. - Read reports for a stretch of normal business days (and a newsletter send if you have one).
- Fix unknown sources: either authorize them or stop them (compromised forms, old CRMs, a well-meaning intern’s new tool).
- Move policy slowly:
quarantinethen, when the picture is clean,rejectif that matches your risk tolerance.
Subdomains can have their own DMARC. Mail from shop.example.com is not automatically covered in the way people assume; check alignment. A default policy on the organizational domain can apply to subdomains unless they override it — read the tags you set and the RFC behavior if you rely on this. When unsure, publish an explicit record on the sending subdomain too.
Reading reports and tightening policy
Aggregate reports list source IPs, whether SPF/DKIM passed, and whether alignment passed. Group by source. Known vendors should pass. Random networks sending as you are why you will eventually want p=reject. Legitimate mail that fails is the danger: a forgotten invoice system. Do not jump to reject the week you publish the record.
When you change to quarantine or reject:
_dmarc.example.com. 300 IN TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc-reports@example.com"
Increase pct over time. Watch customer replies (“I didn’t get your mail”) and your own seed inboxes. Keep TTL modest while you iterate so you can back out. If HTTPS mail links in messages still point at HTTP, that is unrelated to DMARC; fix links and certificates separately.
Forwarding can break SPF (the forwarder’s IP is not in your SPF) while DKIM still passes if the body was not rewritten. That is a reason to have DKIM, not a reason to skip SPF.
Checklist
- One SPF record, no
+all, under 10 DNS lookups, every current sender included. - DKIM published and passing for each sending platform.
- DMARC at none with reports until the sources make sense.
- Unknown senders identified: authorize or eliminate.
- Policy moved in steps toward quarantine/reject without stranding invoices.
Start with inventory and tests, not with p=reject. Authentication is a careful door, not a slam.
Related guides
General technical information; test changes carefully and keep backups of your settings.