When both A and CNAME records exist for the same domain name, DNS resolvers follow strict RFC standards (specifically RFC 1034 Section 3.6.2) which state:
example.com. IN A 192.0.2.1 example.com. IN CNAME alias.example.net.
The CNAME record will always take precedence over the A record. This means the resolver will follow the CNAME chain until it reaches a terminal record (A or AAAA).
Here's what happens during resolution:
- Resolver checks for CNAME first (due to RFC requirements)
- If CNAME exists, the A record is ignored completely
- Resolver follows CNAME to its target
- Process repeats until final A/AAAA record is found
Using Python's socket
module demonstrates this behavior:
import socket def dns_lookup(domain): try: # This will return the CNAME's destination IP, not the A record return socket.gethostbyname('example.com') except socket.gaierror as e: print(f"DNS lookup failed: {e}") print(dns_lookup('example.com')) # Returns the CNAME's IP
Accidental Conflicts: Often happens during DNS migrations when admins forget to remove old A records.
Performance Impact: Each CNAME adds an extra DNS lookup roundtrip. Example measurement:
$ dig +trace example.com ;; Received 85 bytes from 8.8.8.8#53 in 12 ms ;; Received 112 bytes from ns1.provider.com#53 in 24 ms (CNAME) ;; Received 72 bytes from ns2.target.com#53 in 36 ms (A record)
- Never mix A and CNAME records for the same name
- For cloud deployments, prefer ALIAS/ANAME records when available
- Use
dig +trace
or equivalent to verify record hierarchy
When troubleshooting, remember different records may have different TTLs. A working example showing TTL differences:
$ dig example.com ANY ;; ANSWER SECTION: example.com. 300 IN A 192.0.2.1 example.com. 3600 IN CNAME target.example.net.
The lower TTL on the A record (300s) versus CNAME (3600s) can create temporary resolution anomalies during changes.
When a domain has both an A record and CNAME record pointing to different destinations, RFC 1034 Section 3.6.2 explicitly states this creates an invalid DNS configuration. The CNAME record is supposed to be the canonical name for the domain, making all other records at that node (including A records) irrelevant.
In practice, different DNS resolvers handle this conflict differently:
// Example dig command to test resolution
dig example.com ANY +short
// Typical outputs might show:
// - Some resolvers prioritize CNAME
// - Others return both records (technically invalid)
// - Modern cloud providers often reject the configuration entirely
Major DNS providers enforce strict validation:
- AWS Route 53: Returns error "RRSet of type CNAME with DNS name X conflicts with RRSet of type A"
- Cloudflare: Similar validation prevents saving conflicting records
- Google Cloud DNS: Rejects the configuration at API level
Here's what happens in a BIND zone file with conflicting records:
; BIND zone file snippet
example.com. IN A 192.0.2.1
example.com. IN CNAME otherdomain.example.
Running named-checkzone
would output:
"example.com: CNAME and other records" as an error.
To diagnose resolution issues:
# Check authoritative DNS
dig @ns1.example.com example.com ANY
# Trace full resolution path
dig example.com +trace
# Check specific record types
dig example.com A +short
dig example.com CNAME +short
Instead of mixing records, use these patterns:
// Option 1: Use CNAME only
www.example.com. IN CNAME target.example.net.
// Option 2: Use A records with multiple IPs
api.example.com. IN A 192.0.2.1
api.example.com. IN A 192.0.2.2
// Option 3: ALIAS/ANAME records (provider-specific)
// (Cloudflare, DNSimple, etc. implement this differently)
When coding DNS-dependent applications:
// Python example using dnspython
import dns.resolver
try:
answers = dns.resolver.resolve('example.com', 'A')
for rdata in answers:
print('IP', rdata.address)
except dns.resolver.NoAnswer:
try:
cname = dns.resolver.resolve('example.com', 'CNAME')
print('CNAME', cname[0].target)
except dns.resolver.NoAnswer:
print('No records found')