A CNAME owns the whole name
example.com is an alias for site.platform.example, an MX record sitting next to the alias is a contradiction. Do you use the local MX or the target's MX? The protocol avoids the question by not allowing both.
The root of a zone, also called the apex, makes this worse. Every zone apex has to carry an SOA record (the zone's metadata) and NS records (which servers are authoritative). So a CNAME at the apex always conflicts with something, even before you add mail records. The problem isn't limited to the apex, though. A CNAME on any subdomain that also needs MX or TXT, such as a mail subdomain or a name used for domain verification, breaks the same way.
What a resolver does when it meets a CNAME
[email protected] and asks its recursive resolver for the MX records of example.com.
- The resolver works its way down to the authoritative nameserver for
example.comand asks for type MX at that name. - The authoritative server looks up the name and finds a CNAME. Under the standard lookup algorithm in RFC 1034, it doesn't go looking for MX at that name. It returns the CNAME, and if it is also authoritative for the target, it may include the target's answer too.
- The resolver sees the CNAME and restarts the same question with the new name: what are the MX records for
site.platform.example? - Whatever the target zone says becomes the answer for
example.com. If the target has no MX records, the result is NOERROR with no MX data, usually called NODATA.
example.com gets the TXT records of the CDN hostname, which usually have no SPF policy for your domain at all. A verification service looking for its token gets someone else's TXT set, or nothing. From their side, your record doesn't exist.
Caching adds a delay on top. Resolvers keep the CNAME for its TTL, and they can also cache the NODATA result for the target according to that zone's negative caching settings (RFC 2308). Fixing the record removes the cause right away, but some resolvers keep the old answer until their cache expires.
Why mail breaks in a specific way
example.com, follows the same CNAME, and gets the IP addresses of the web platform.
So the sending server tries to open an SMTP connection on port 25 to a CDN or web host that was never meant to accept mail. The connection may be refused, may time out, or may reach something that rejects the recipient. Most MTAs treat connection problems as temporary, so messages sit in the sender's queue and retry for a while before bouncing. That is why it often gets reported as 'mail is slow or missing' rather than a clean error. On the support desk, this kind of report usually comes with a recent DNS change for the website, and the person who made that change rarely connects it to email.
Spotting the conflict with dig
dig queries cover both. Replace the domain and the nameserver with your own values.
BASH
# What a resolver returns for mail and TXT lookups
dig example.com MX +noall +answer
dig example.com TXT +noall +answer
# Is there a CNAME on the name itself?
dig example.com CNAME +short
# Find the authoritative servers, then ask one directly (no cache involved)
dig example.com NS +short
dig @ns1.example-dns.net example.com MX +norecurse +noall +answerTEXT
; Illustrative output format
example.com. 300 IN CNAME site.platform.example.dig ANY for this check. Many servers now return a minimal or refused response to ANY queries, so a short answer there proves nothing. Asking for each record type separately is more reliable.
Alternatives that keep the apex valid
www at the platform with a CNAME, keep only address records or nothing web-related at the apex, and redirect the bare domain to www. This is often the cleanest setup when the platform requires a CNAME.
TEXT
; Conflicting: the alias shares a name with mail records
example.com. IN CNAME site.platform.example.
example.com. IN MX 10 mail.example.com.
; Valid: apex keeps address and mail records, alias moves to www
example.com. IN A 192.0.2.10
example.com. IN MX 10 mail.example.com.
example.com. IN TXT 'v=spf1 mx -all'
www.example.com. IN CNAME site.platform.example.www. Then confirm against the authoritative server, and give caches one TTL cycle before deciding whether it worked.