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, which the documentation describes bluntly: it “prevents email delivery.”

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 states that “mail protocols such as SMTP, IMAP, and POP3 do not work through Cloudflare’s standard HTTP proxy.” Port 25 is not proxied by default, and only a separate product (Spectrum) can proxy SMTP at all.

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 a lot of secondary sources are wrong, so here is the primary version. Google’s own guidance states that from 1 February 2024, senders sending more than 5,000 messages a day to Gmail accounts 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 widely misquoted: the date 1 June 2024 does not appear on Google’s official guidance page, and — the opposite of what most summaries say — p=none is explicitly acceptable for the DMARC requirement. The announcement blog from October 2023 does not contain the SPF, DKIM, DMARC, or 0.30% details at all; only the help-centre page does.

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. 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.


Written by TestedHost. Every recommendation on this site comes from running the setup described, on a live deployment — not from a vendor spec sheet. Spotted something out of date? Tell us.