Domains & DNS

How to Set DNS Records Without Breaking Your Email

Moving DNS between providers is presented as a copy-and-paste job. It is a copy-and-paste job until you have email on the domain, at which point one wrong toggle can stop mail arriving for hours, and the dashboard will not tell you why.

The failure is almost never subtle. It is almost always a mail-related record sitting behind a proxy it should never have been behind.

The rule that matters most

Cloudflare’s documentation is unambiguous about which record types can be proxied: only A, AAAA, and CNAME are eligible, and “other record types (such as MX or TXT) are always DNS-only.”

So far so safe — MX cannot be proxied, so you cannot accidentally proxy it. But the guidance continues, and this is the part people miss:

“MX records cannot be proxied. If an A or AAAA record is used exclusively for email (for example, mail.example.com), it should also be set to DNS-only.”

The MX record points at a hostname. That hostname is usually an A record, and that A record can be proxied. Do that and mail servers connect to Cloudflare’s IP addresses instead of your mail server, so the mail never reaches your mailbox — which is exactly why the record types that handle mail are excluded from the proxy in the first place.

The same page spells out the general case: “Your MX record itself is always DNS-only, but the hostname it points to must also resolve to a DNS-only target.”

The mechanism is worth internalising. A proxy answers with an HTTP-aware edge; mail is not HTTP. Cloudflare’s own wording is narrower than the folk version of this rule: it does not proxy email traffic on port 25 by default, and the only way to proxy outgoing SMTP is a separate product — Spectrum.

Practical rule: any hostname involved in mail is DNS-only, grey cloud, always. If you are not sure whether a hostname is involved in mail, check whether it appears on the right-hand side of an MX record or in a mail client’s configuration.

The _dc-mx rewrite you’ll find in your own output

One behaviour that generates unnecessary panic during a migration: if your MX record points at the same hostname as your website, Cloudflare dynamically prepends _dc-mx to that hostname in the MX response.

So you move DNS, run dig MX, and see a hostname you never typed. It is documented behaviour, not an attack, and not a misconfiguration you caused. The important nuance is that it applies when mail and web share a hostname — which is itself a design smell. A cleaner setup gives mail its own hostname so that the proxies and the mail delivery paths never touch.

SPF is a budget of ten DNS lookups

SPF’s reputation for being confusing comes almost entirely from one rule most people never read.

Under RFC 7208, the mechanisms and modifiers that trigger DNS lookups are counted together, and the total must not exceed 10. Exceed it and you do not get degraded service — you get a permerror, which is a broken record.

Counts toward the 10Does not count
includeip4
aip6
mxall
ptrexp
exists
redirect modifier

Three details that change how you write the record:

It is a single global limit, not per-include. So each include: costs you not one lookup but potentially several, because the included domain’s own SPF record is evaluated against the same budget. A record with four includes can exhaust the ten before you have written anything else.

mx and ptr have additional limits. They resolve to a set of addresses, each of which costs a lookup in practice.

RFC 7208 also recommends a limit of two “void lookups” — lookups that return no answer. Void lookups are how a syntactically valid record still fails.

None of the provider documentation I could find tells you how many lookups a given third-party service’s record actually consumes. Cloudflare’s email records page gives example records for major providers without annotating their lookup cost. Counting them is your job, which is an unfortunate amount of work for a rule whose penalty is total failure.

The -all versus ~all decision

The closing mechanism is the one place where the syntax carries intent:

  • -all means fail. Receivers may reject outright — typically a 550 5.7.1.
  • ~all means softfail. RFC 7208 is explicit that a softfail is “a weak statement by the publishing ADMD that the host is probably not authorized,” and that receivers “SHOULD NOT reject the message based solely on this result.”

If your goal is to prevent spoofing, ~all is weak by design. If your goal is to avoid breaking a legitimate sender you forgot about, -all is a loaded gun. The transition most senders make is ~all first, watch the reports, then -all.

Multiple SPF records are always broken

RFC 7208 is unambiguous: “multiple SPF records are not permitted for the same owner name.” Two TXT records starting with v=spf1 on the same domain is a permerror — all of them, together, stop working. Not the second one; all of them.

This is the most common way an inherited DNS zone is already broken before you touch it. It happens when two providers are configured, each helpfully adding its own SPF record. Merging them into one record is the fix, and it is why the lookup budget matters: merged records are longer.

DKIM and DMARC are one record each

DKIM (RFC 6376) puts a public key in a TXT record at <selector>._domainkey.<domain>. TXT is the only option the base specification defines. The p= tag is required and holds the base64 key — and an empty p= value is the documented way to revoke a key. v=DKIM1 must be the first tag in the record.

DMARC (RFC 7489) lives at _dmarc.<domain> as a TXT record. Three policy values — p=none, p=quarantine, p=reject — plus rua for aggregate reports and ruf for failure reports. A message passes DMARC if either SPF or DKIM passes and aligns with the From domain; the alignment modes adkim and aspf default to relaxed.

One correction to the common advice: RFC 7489 does not contain the recommendation to start at p=none. What it actually says is narrower — that a discovered policy of p=none “SHOULD NOT modify existing mail disposition processing” — alongside a pct mechanism for gradually rolling out a policy. “Start with p=none” is sound operational practice, but it is community consensus, not a specification requirement.

What the 2024 Gmail rules actually require

This is where summaries diverge, so here are the numbers worth holding onto. The requirement that took effect on 1 February 2024 applies to senders putting more than 5,000 messages a day into Gmail accounts. Those senders must:

  • Configure SPF and DKIM on the domain
  • Have DMARC, with a policy of p=none being acceptable
  • Have valid forward and reverse DNS (a PTR record)
  • Send over TLS
  • Keep a spam complaint rate below 0.30% in Postmaster Tools
  • Comply with RFC 5322, and not spoof Gmail’s From header
  • Ensure the From domain aligns with either the SPF or the DKIM domain
  • Provide one-click unsubscribe plus a visible unsubscribe link for marketing messages

All senders, regardless of volume, need SPF or DKIM.

Two things get conflated in summaries. The bulk sender requirements took effect on 1 February 2024; a later June 2024 date in the same programme covers one-click unsubscribe for those same senders, which is where the second date comes from. And p=none is acceptable for the DMARC requirement at this stage — it is a monitoring policy, not a failed one. The wording of these rules is revised from time to time, so check it against your own sending volume rather than against a summary written in a previous year.

The migration order nobody documents

Here is the gap. Cloudflare publishes a careful, order-sensitive migration procedure for DNSSEC. For MX, there is no equivalent. There is a troubleshooting page for email issues, and a page acknowledging that Email Routing records “may conflict with your provider’s records” — but no documented sequence for “I am moving DNS providers and I must not drop mail.”

So here is the sequence I would use, derived from the constraints above:

  1. Raise the TTL on your MX record to 24 hours, and wait a day. This is the single step that makes everything else recoverable, and it is the same rule any record move follows. It costs one day and buys you a fast rollback window.
  2. Write down every mail-related record before touching anything — MX, SPF, DKIM selectors, DMARC, and the A records those MX hosts point to. Export the zone, do not transcribe it.
  3. Create the same records at the new provider, including the mail hostnames, set to DNS-only. Do not change nameservers yet.
  4. Verify from outside using dig @1.1.1.1 and dig @8.8.8.8 against the new nameservers directly — you can query a specific nameserver before it is authoritative.
  5. Switch nameservers, then immediately confirm MX and TXT resolve from a public resolver.
  6. Send a test message in and out, and check the received headers for SPF and DKIM results. A pass on both is the confirmation that matters; a 250 response from the SMTP server is not.
  7. Lower the TTL back once you are satisfied.

The reason step 1 comes first: if the nameserver switch goes wrong, you want the old values still cached while you revert, rather than watching every resolver pick up the broken state within minutes.

When to stop and pay someone

If the domain sends transactional mail that customers depend on — password resets, order confirmations — a self-managed DNS migration is a real risk with a real blast radius. The tooling is not expensive, and the failure mode is not “email is a bit late,” it is “nobody can log in for six hours.”

For a personal site with one mailbox, the checklist above is enough. The distinction is not technical difficulty. It is what happens to other people while you are debugging.

Sources

Primary documentation only — the provider's own page, the specification, or the standard itself. Claims that rest on anything else are described in the text as a market pattern or an inference, not as documentation.

  1. Cloudflare — Proxied DNS records (which record types are eligible for the proxy; MX and TXT are always DNS-only)
  2. Cloudflare — Set up email records (port 25 is not proxied by default; Spectrum is the only way to proxy outgoing SMTP)
  3. RFC 7208 — Sender Policy Framework (the 10-lookup budget, the two-void-lookup recommendation, the single-record rule, and the softfail wording quoted above)
  4. RFC 6376 — DomainKeys Identified Mail (selector._domainkey placement, the required p= tag, and key revocation via an empty p=)
  5. RFC 7489 — DMARC (the _dmarc record, policy values, alignment modes, and the p=none wording quoted above)

Written by . Spotted something out of date? Tell us.