exim.ro

Email DNS records: MX, SPF, DKIM and DMARC explained

What MX, SPF, DKIM, DMARC, MTA-STS, TLS-RPT and BIMI records do, how receiving servers read them, and the mistakes that break delivery.

Updated 8 min read8 sections

Run the Email DNS check

Whether a message is delivered, sent to spam or rejected depends to a large degree on a handful of DNS records published by the sender's and the recipient's domains. This guide explains what each one does and how receiving servers use it. The Email DNS check reads all of them for a domain and applies the rules described here.

MX: where mail for a domain goes

An MX record names a host that accepts mail for the domain, with a priority number. Senders try the lowest number first and fall back to higher ones when a host does not answer; hosts with the same number share the load.

example.com.  3600  IN  MX  10 mx1.example.com.
example.com.  3600  IN  MX  20 mx2.example.com.

Rules worth knowing:

  • The target must be a host name with its own A or AAAA record. An IP address is not allowed, and RFC 2181 (section 10.3) forbids pointing an MX at an alias (CNAME).
  • Without any MX record, senders deliver to the A/AAAA address of the domain itself (RFC 5321, section 5.1). This works only if that host runs a mail server.
  • A domain that never receives mail should publish a null MX, a single record 0 . (RFC 7505). Mail to it is rejected at once instead of being retried for days.

SPF: who may send as the domain

SPF (RFC 7208) is a TXT record at the domain that lists the servers allowed to send mail with that domain in the envelope sender (the MAIL FROM address, also called Return-Path).

example.com.  IN  TXT  "v=spf1 ip4:192.0.2.0/24 include:_spf.provider.example ~all"

The receiver compares the connecting server's IP address with the terms from left to right and stops at the first match. The qualifier in front of a term decides the result: + pass (the default), - fail, ~ softfail, ? neutral.

TermMatches
ip4:, ip6:an address inside the given network
a, a:hostthe A/AAAA addresses of the domain or of host
mx, mx:hostthe addresses of the domain's MX hosts
include:domainwhatever the SPF record of domain allows
exists:nameif name has an address record (used with macros)
alleverything; it belongs at the end
redirect=domainuse another domain's record if nothing matched

The limit of 10 DNS lookups

The terms include, a, mx, ptr, exists and the redirect modifier each cause DNS queries. RFC 7208 (section 4.6.4) caps them at 10 for the whole evaluation, nested includes included. A receiver that reaches the 11th returns permerror, and DMARC treats that as a failure. The count is easy to exceed without noticing, because every provider's include brings its own includes. The checker follows the whole tree and adds them up.

Two further limits from the same section: an mx term may not return more than 10 MX records, and no more than two lookups may come back empty ("void lookups": NXDOMAIN or no data).

Common SPF mistakes

  • Two SPF records. A domain must publish exactly one TXT record starting with v=spf1; with two, every check ends in permerror. Merge them.
  • +all authorises every server on the Internet. ?all says nothing. Use ~all or -all.
  • ptr is slow and unreliable and should not be used (RFC 7208, section 5.5).
  • Terms after all are never evaluated.
  • A domain that sends no mail at all should publish v=spf1 -all.

DKIM: a signature that survives forwarding

DKIM (RFC 6376) lets the sending server sign parts of each message with a private key. The signature header names a selector (s=) and a domain (d=); the receiver fetches the public key from selector._domainkey.domain and verifies that the signed headers and body were not changed.

s1._domainkey.example.com.  IN  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqh..."
  • Selectors cannot be listed. DNS has no way to enumerate them, so a checker can only try common names. If none is found, look at the s= value in the DKIM-Signature header of a real message and check that selector.
  • Key size. RFC 8301 requires verifiers to reject RSA keys under 1024 bits and recommends at least 2048 bits for signers.
  • t=y marks a key as in testing: receivers may treat failed signatures as if the message were unsigned.
  • An empty p= means the key has been revoked.
  • Ed25519 keys (k=ed25519, RFC 8463) are smaller, but not every receiver verifies them yet, so they are usually published alongside an RSA key.

DMARC: what to do when checks fail

DMARC (RFC 7489) ties SPF and DKIM to the address people see, the From: header. A message passes DMARC when SPF or DKIM passes and the domain it authenticated is aligned with the From domain. The record at _dmarc.domain tells receivers what to do with mail that fails, and where to send reports.

_dmarc.example.com.  IN  TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"
TagMeaning
p=policy for the domain: none (report only), quarantine (treat as suspicious), reject
sp=policy for subdomains; if absent, p= applies
pct=percentage of failing mail the policy applies to (default 100)
rua=where aggregate reports go
ruf=where failure reports for single messages go; few receivers send them
adkim=, aspf=alignment: r relaxed (subdomains match, the default) or s strict

How a policy is usually rolled out:

  1. Publish p=none with a rua= address and read the reports for a few weeks.
  2. Fix every legitimate source that fails (a newsletter service without DKIM, a server missing from SPF).
  3. Move to p=quarantine, then to p=reject.

Things that go wrong:

  • No rua=: there are no reports, so you cannot see who sends as your domain.
  • Reports to another domain: if rua= points outside your domain, that domain must publish yourdomain._report._dmarc.theirdomain with v=DMARC1, or receivers will not send the reports (RFC 7489, section 7.1). Report services normally do this for you.
  • More than one DMARC record: receivers then apply no DMARC at all.
  • Subdomains: with no record of its own, a subdomain is covered by the parent domain's record and its sp= policy.

MTA-STS: make senders insist on TLS

Mail between servers is usually encrypted with STARTTLS, but an attacker on the path can remove the offer and read the traffic. MTA-STS (RFC 8461) lets a domain declare that senders must use TLS with a valid certificate, and only towards listed MX hosts. It has two parts: a TXT record at _mta-sts.domain with a version and an id, and a policy file served over HTTPS at https://mta-sts.domain/.well-known/mta-sts.txt:

version: STSv1
mode: enforce
mx: mx1.example.com
mx: *.example.net
max_age: 1209600
  • mode: testing reports problems but still delivers; enforce refuses delivery that does not meet the policy.
  • Every MX host must match an mx: line, where *. stands for exactly one label. An MX added without updating the policy stops receiving mail from enforcing senders.
  • The policy must be answered with HTTP 200, without redirects, and over a valid certificate for mta-sts.domain.
  • max_age is at most 31557600 seconds (one year); RFC 8461 expects values of weeks or more.
  • Change the id in the TXT record whenever the policy changes, so senders fetch it again.

TLS-RPT: hear about TLS failures

With a TXT record at _smtp._tls.domain (RFC 8460), sending servers send you a daily report of failed TLS connections to your servers.

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

It is optional on its own, but with MTA-STS in enforce mode it is the way to learn that an expired certificate is blocking mail.

BIMI: a logo next to authenticated mail

BIMI lets mailboxes that support it show a brand logo next to messages that pass DMARC. The record lives at default._bimi.domain; l= points to the logo, an SVG file in the SVG Tiny Portable/Secure profile, and a= to a mark certificate that several large mailbox providers require.

default._bimi.example.com.  IN  TXT  "v=BIMI1; l=https://example.com/logo.svg; a=https://example.com/vmc.pem"

BIMI is only processed when the DMARC policy is enforced: not p=none, not sp=none, and with pct at 100 (draft-brand-indicators-for-message-identification, section 7.1).

Sources