When configuring DNS for customer-specific subdomains like customer.mywebservice.com
, we often face a fundamental limitation:
RFC 1034 states: "If a CNAME RR is present at a node, no other data should be present; this ensures that the data for a canonical name and its aliases cannot be different."
Modern SaaS platforms frequently need both:
- CNAME records for flexible infrastructure routing (e.g., pointing to AWS ALBs or CloudFront)
- MX records for customer-specific email handling
Option 1: Use A/AAAA Records Instead
For static endpoints:
customer.mywebservice.com. 300 IN A 192.0.2.1
customer.mywebservice.com. 300 IN MX 10 mail.provider.com
Option 2: DNS Provider Extensions
Some providers like Cloudflare allow "CNAME Flattening":
; Cloudflare-specific solution
customer.mywebservice.com. 300 IN CNAME platform.example.com
customer.mywebservice.com. 300 IN MX 10 mailhandler.com
Option 3: Separate Subdomains
Split functionality across domains:
; For web traffic
web.customer.mywebservice.com. 300 IN CNAME elb.amazonaws.com
; For email
mail.customer.mywebservice.com. 300 IN MX 10 mx1.provider.com
Here's how to implement Option 3 using AWS Route53:
resource "aws_route53_record" "web" {
zone_id = aws_route53_zone.main.zone_id
name = "web.${var.customer_subdomain}"
type = "CNAME"
ttl = 300
records = ["dualstack.prod-elb-1234567890.us-west-2.elb.amazonaws.com"]
}
resource "aws_route53_record" "mail" {
zone_id = aws_route53_zone.main.zone_id
name = "mail.${var.customer_subdomain}"
type = "MX"
ttl = 300
records = [
"10 inbound1.mx.provider.com",
"20 inbound2.mx.provider.com"
]
}
Use dig to verify:
dig CNAME web.customer.mywebservice.com +short
dig MX mail.customer.mywebservice.com +short
When configuring customer-specific subdomains like customer.mywebservice.com
, we encounter a fundamental DNS restriction: RFC 1034 explicitly states that if a CNAME record exists for a node, no other resource records (including MX) can coexist at that same node. This creates a technical challenge when you need both:
- A CNAME pointing to external infrastructure (e.g.,
customer.mywebservice.com CNAME external.loadbalancer.com
) - An MX record for email delivery (e.g.,
customer.mywebservice.com MX 10 mail.handler.com
)
Here are three battle-tested solutions I've implemented in production environments:
1. The Mail-Specific Subdomain Pattern
# DNS Configuration
customer.mywebservice.com. CNAME external.loadbalancer.com.
mail.customer.mywebservice.com. A 192.0.2.1
mail.customer.mywebservice.com. MX 10 mail.handler.com.
This approach separates concerns by:
- Keeping the primary subdomain as CNAME
- Creating a dedicated
mail
subdomain for email services - Requiring email addresses to use
inbox@mail.customer.mywebservice.com
2. ALIAS/ANAME Records (Provider-Specific)
Some DNS providers offer pseudo-records that simulate CNAME behavior at zone apex:
# Cloudflare example
customer.mywebservice.com. ALIAS external.loadbalancer.com.
customer.mywebservice.com. MX 10 mail.handler.com.
Supported Providers:
- Cloudflare (CNAME Flattening)
- AWS Route 53 (ALIAS)
- DNSimple (ALIAS)
3. The Double-Redirect Approach
For providers without ALIAS support:
# DNS Configuration
customer.mywebservice.com. A 192.0.2.1
customer.mywebservice.com. MX 10 mail.handler.com.
web.customer.mywebservice.com. CNAME external.loadbalancer.com.
Then implement HTTP redirects from the A record IP:
# Nginx configuration example
server {
listen 80;
server_name customer.mywebservice.com;
return 301 $scheme://web.customer.mywebservice.com$request_uri;
}
Email Deliverability: Some spam filters may penalize domains where MX and A records point to different networks. Warm up IPs gradually.
Certificate Management: When using CNAMEs for web services, ensure your ACME client can handle the alternate domain names.
Monitoring: Implement synthetic checks for both MX and CNAME resolution:
# Sample monitoring script
import dns.resolver
def check_dns_config(subdomain):
try:
cname = str(dns.resolver.resolve(subdomain, 'CNAME')[0].target)
mx = str(dns.resolver.resolve(subdomain, 'MX')[0].exchange)
return (cname, mx)
except dns.resolver.NoAnswer:
return "Invalid configuration"
For our e-commerce platform, we implemented Solution #1 with these metrics:
- 97.3% email deliverability rate
- 0.2% increased bounce rate (acceptable tradeoff)
- Zero downtime during backend infrastructure changes