Introduction
CNAME (Canonical Name) records create aliases that point one domain name to another. While simple in concept, CNAME records have strict rules that cause problems when violated. The most common issue is attempting to use a CNAME at the apex (root) of a domain, which conflicts with other required records. CNAME chains, circular references, and conflicts with other record types also cause resolution failures.
Symptoms
- Website works when accessed directly but fails via CNAME alias
- CNAME at domain apex causes email or other services to break
- DNS resolution returns SERVFAIL or CNAME loop errors
- dig shows CNAME but the target doesn't resolve properly
- Cloudflare, AWS, or CDN proxy settings cause CNAME flattening issues
- MX, TXT, or other records don't work when CNAME exists at same name
Common Causes
- CNAME at domain apex (root) conflicting with SOA, NS, and other required records
- CNAME chain too long or circular (A points to B, B points to C, C points to A)
- CNAME target doesn't exist or doesn't resolve
- CNAME coexisting with other record types at the same name (invalid)
- CNAME pointing to external domain you don't control that changed
- Provider-specific CNAME flattening or proxy features interfering
Step-by-Step Fix
- 1.Identify all CNAME records in your zone and their targets.
```bash # Query for CNAME records dig @ns1.yourprovider.com example.com CNAME dig @ns1.yourprovider.com www.example.com CNAME
# List all CNAMEs in your zone dig @ns1.yourprovider.com example.com AXFR | grep CNAME
# Or use host command host -t CNAME www.example.com ns1.yourprovider.com ```
- 1.Check if CNAME exists at the domain apex (root) - this is a common problem.
```bash # Check for CNAME at apex (example.com, not www.example.com) dig @ns1.yourprovider.com example.com CNAME +short
# If this returns a value, you have a CNAME at apex # This is INVALID because apex needs SOA, NS, MX records
# Verify other records exist at apex (they can't with CNAME) dig @ns1.yourprovider.com example.com SOA dig @ns1.yourprovider.com example.com NS dig @ns1.yourprovider.com example.com MX
# CNAME at apex means NO other records can exist # But apex MUST have SOA and NS records # This creates a conflict ```
- 1.For apex CNAME issues, use provider-specific solutions instead.
```bash # CNAME at apex is technically invalid per DNS RFC # Solutions by provider:
# Cloudflare: Enable CNAME flattening # Dashboard -> DNS -> Settings -> CNAME Flattening # Or use "Orange Cloud" proxy mode
# AWS Route 53: Use Alias records instead of CNAME # Alias records work at apex and point to AWS resources
# Azure DNS: Use alias record sets # Supported for Azure CDN, Traffic Manager, etc.
# Google Cloud DNS: No native solution, use A records # Or use HTTP redirect at apex
# Traditional DNS: Use A record at apex, CNAME for www example.com. IN A 192.0.2.1 www.example.com. IN CNAME example.com. ```
- 1.Verify CNAME target resolves correctly and has proper records.
```bash # Check CNAME target resolution dig @ns1.yourprovider.com www.example.com CNAME +short # Returns: example-cdn.cloudfront.net.
# Now resolve the target dig example-cdn.cloudfront.net A +short # Should return IP addresses
# If target doesn't resolve, the CNAME chain is broken # Check target's authoritative servers: dig example-cdn.cloudfront.net @ns-1234.awsdns-12.com ```
- 1.Detect CNAME chains and circular references.
```bash # Follow the CNAME chain manually hostname="www.example.com" while true; do cname=$(dig @8.8.8.8 $hostname CNAME +short | head -1) if [ -z "$cname" ]; then echo "Chain ends at: $hostname" dig @8.8.8.8 $hostname A +short break fi echo "$hostname -> $cname" hostname=$cname
# Safety: break after 10 iterations if [ $(echo $hostname | grep -c '\.') -gt 10 ]; then echo "Possible loop detected" break fi done
# dig with +trace shows the chain dig www.example.com +trace ```
- 1.Check for CNAME conflicts with other record types at the same name.
```bash # CNAME cannot coexist with ANY other record type at the same name # This is INVALID: # sub.example.com. IN CNAME target.example.com. # sub.example.com. IN A 192.0.2.1 # sub.example.com. IN MX 10 mail.example.com.
# Check for conflicts for type in A AAAA MX TXT NS SRV; do result=$(dig @ns1.yourprovider.com sub.example.com $type +short) if [ -n "$result" ]; then echo "Found $type record at same name as CNAME - CONFLICT" fi done
# The only exception is DNSSEC records (NSEC, RRSIG, etc.) ```
- 1.Test CNAME resolution end-to-end through public resolvers.
```bash # Test complete resolution chain echo "Step 1: Get CNAME" cname=$(dig @8.8.8.8 www.example.com CNAME +short) echo "CNAME: $cname"
echo "Step 2: Resolve target" target_ip=$(dig @8.8.8.8 $cname A +short | head -1) echo "Target IP: $target_ip"
echo "Step 3: Verify connectivity" curl -I -H "Host: www.example.com" http://$target_ip
# Use dig's built-in chain following dig @8.8.8.8 www.example.com +show +additional ```
- 1.Fix CNAME records that point to incorrect or dead targets.
```bash # If target changed, update the CNAME # Example: CDN endpoint changed
# Check current CNAME dig www.example.com CNAME +short # Returns: old-cdn.cloudfront.net.
# Verify new target works dig new-cdn.cloudfront.net A +short # Should return valid IPs
# Update the CNAME record in DNS # BIND zone file: www.example.com. 3600 IN CNAME new-cdn.cloudfront.net.
# Increment serial and reload # For BIND: rndc reload example.com ```
- 1.Handle CNAME for services with specific requirements.
```bash # For email, never use CNAME for MX hostname # WRONG: mail.example.com. IN CNAME mail.provider.com. example.com. IN MX 10 mail.example.com.
# RIGHT: mail.example.com. IN A 192.0.2.10 example.com. IN MX 10 mail.example.com.
# For DKIM/TXT records, CNAME can work selector1._domainkey.example.com. IN CNAME selector1._domainkey.provider.com.
# Verify DKIM CNAME resolves dig selector1._domainkey.example.com TXT +short ```
- 1.Implement monitoring for CNAME target changes.
```bash # Create a simple monitoring script #!/bin/bash EXPECTED_CNAME="example-cdn.cloudfront.net." DOMAIN="www.example.com"
current_cname=$(dig @8.8.8.8 $DOMAIN CNAME +short) if [ "$current_cname" != "$EXPECTED_CNAME" ]; then echo "WARNING: CNAME changed for $DOMAIN" echo "Expected: $EXPECTED_CNAME" echo "Current: $current_cname" # Send alert fi
# Check target resolves if [ -n "$current_cname" ]; then target_ip=$(dig @8.8.8.8 ${current_cname%.} A +short) if [ -z "$target_ip" ]; then echo "ERROR: CNAME target does not resolve!" fi fi ```
Verification
After fixing CNAME issues, verify complete resolution:
```bash # 1. Verify CNAME points to correct target dig @ns1.yourprovider.com www.example.com CNAME +short
# 2. Follow complete resolution chain dig www.example.com +trace
# 3. Test from multiple resolvers for resolver in 8.8.8.8 1.1.1.1 9.9.9.9; do echo "Resolver $resolver:" dig @$resolver www.example.com +short done
# 4. Verify no apex CNAME issues dig example.com SOA +short dig example.com NS +short # These must return values (can't have CNAME at apex with SOA/NS)
# 5. Test actual HTTP resolution curl -I https://www.example.com ```
CNAME Best Practices Summary
- Never put CNAME at domain apex (use A records or provider alias)
- Ensure CNAME target exists and resolves
- Keep CNAME chains short (ideally 1 hop, max 2-3)
- Don't mix CNAME with other record types at same name
- Monitor CNAME targets for changes
- Use ALIAS/ANAME records if provider supports them for apex
- Remember CNAME has performance cost (extra resolution step)