SPF Records and CNAMEs: Compatibility, Best Practices, and Validation Techniques


5 views

There's persistent confusion in the DNS community about whether SPF records work correctly on domains that are CNAMEs. The RFC 7208 (SPF) states that SPF records should be placed on the canonical name, not the aliased one, yet real-world implementations show more nuanced behavior.

When a mail server checks SPF for pi.dantup.com (a CNAME to dantuppi.dynv6.net), the resolution typically follows this path:

1. Query TXT records for pi.dantup.com
2. Find SPF record if exists
3. If no SPF, follow CNAME chain looking for TXT records
4. If still no SPF, return "none" result

Your specific configuration succeeds because:

  • The SPF record exists directly on the CNAME domain (pi.dantup.com)
  • Most SPF validators check the original domain first before following CNAMEs
  • The dynv6.net domain doesn't have conflicting SPF records

Several factors can break this setup:

1. Some older DNS resolvers might skip TXT records on CNAME domains
2. If the target domain (dynv6.net) had SPF records, they might override yours
3. Certain email providers might implement stricter CNAME resolution

For production environments, I recommend:

; Preferred solution - use A/AAAA records instead of CNAME
pi.dantup.com.  IN  A  192.0.2.1
pi.dantup.com.  IN  TXT "v=spf1 a -all"

; If you must use CNAME, ensure SPF on both domains
pi.dantup.com.  IN  CNAME dantuppi.dynv6.net.
dantuppi.dynv6.net.  IN  TXT "v=spf1 a -all"

Use these diagnostic commands:

# Check all record types
dig pi.dantup.com ANY

# Explicit TXT lookup
dig pi.dantup.com TXT

# Follow CNAME chain
dig +trace pi.dantup.com CNAME

For automated monitoring, consider this Python check:

import dns.resolver

def check_spf(domain):
    try:
        answers = dns.resolver.resolve(domain, 'TXT')
        spf_records = [r.to_text() for r in answers if 'v=spf1' in r.to_text()]
        return bool(spf_records)
    except dns.resolver.NoAnswer:
        return False

In DNS architecture, CNAME records create an alias that points to another domain name, while SPF (Sender Policy Framework) records define which mail servers are authorized to send emails for a domain. The fundamental conflict arises because:

example.com.    IN CNAME target.example.net.
example.com.    IN TXT "v=spf1 include:_spf.google.com ~all"

RFC 7208 (SPF) Section 5 explicitly states that when a domain has both CNAME and other records, the CNAME takes precedence, making other records unreachable.

Your specific case with pi.dantup.com appears to work because:

  • The CNAME resolution happens at a different layer than SPF checking
  • Some mail servers may cache the TXT records independently
  • DNS propagation delays can create temporary inconsistencies

Let's examine the DNS lookup chain using dig:

dig pi.dantup.com CNAME +short
# Returns: dantuppi.dynv6.net

dig pi.dantup.com TXT +short
# Returns: "v=spf1 a mx ~all"

This shows both records coexist, but according to RFCs, the CNAME should make the TXT record inaccessible.

Here are three architecturally sound approaches:

1. Replace CNAME with A/AAAA Records

; Instead of:
; sub.domain.com. IN CNAME target.example.net

; Use:
sub.domain.com.  IN A 192.0.2.1
sub.domain.com.  IN TXT "v=spf1 include:domain.com ~all"

2. SPF via Parent Domain

domain.com.      IN TXT "v=spf1 include:spf.protection.outlook.com -all"
*.domain.com.    IN TXT "v=spf1 include:domain.com ~all"

3. Modern DNS Providers Solution

Some DNS providers implement workarounds:

; Cloudflare example
sub.domain.com.  IN CNAME target.example.net
sub.domain.com.  IN TXT "v=spf1 redirect=domain.com"

Always validate your setup with:

nslookup -type=TXT yourdomain.com
dig +short TXT yourdomain.com

For comprehensive testing:

python3 -m spfcheck email@yourdomain.com 192.0.2.1