SSL Certificate Compatibility with CNAME Aliases: Wildcard Coverage for Custom Domains


6 views

When implementing multi-tenant architectures with user-specific subdomains (e.g., user1.mysite.com), developers often use wildcard SSL certificates (*.mysite.com) for HTTPS coverage. The critical question arises when users bring their own domains via CNAME records (e.g., user-brand.com pointing to user1.mysite.com).

Wildcard certificates only cover domains exactly matching their pattern. A certificate for *.test.com won't validate custom-domain.com even if it CNAMEs to mysite.test.com. The SSL/TLS handshake validates against the requested domain's name in the SNI field, not the resolved CNAME target.

Here are three approaches with code examples:

1. SAN Certificates

Use Subject Alternative Name certificates covering both wildcard and custom domains:

# OpenSSL config for SAN cert
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = *.test.com
DNS.2 = custom-domain1.com
DNS.3 = custom-domain2.com

2. Reverse Proxy with SNI Routing

Nginx example routing different domains to same backend:

server {
    listen 443 ssl;
    server_name custom-domain.com;
    ssl_certificate /path/to/custom-domain.crt;
    ssl_certificate_key /path/to/custom-domain.key;
    location / {
        proxy_pass https://user1.test.com;
    }
}

3. Automated Certificate Management

Using Let's Encrypt with DNS challenges for dynamic domains:

# Certbot with DNS plugin
certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d custom-domain.com \
  --preferred-challenges dns-01

Each solution has trade-offs:

  • SAN certs: Single cert management but requires reissuing for new domains
  • Reverse proxy: Added latency (~5-15ms per hop)
  • Automated certs: Requires DNS API access and has rate limits

Always:

  • Set proper certificate transparency logs
  • Implement HSTS with includeSubDomains
  • Rotate certificates at least quarterly

When implementing multi-tenant SaaS architectures, developers often face domain management challenges. A common pattern is:

1. Primary domain: test.com
2. User subdomains: user1.test.com (covered by *.test.com wildcard SSL)
3. Custom CNAMEs: customer.com → user1.test.com

The SSL/TLS handshake occurs before any HTTP redirection takes place. When a client connects to customer.com:

  1. Server presents certificate for *.test.com
  2. Browser verifies certificate against customer.com
  3. Mismatch triggers security warning

Here are three approaches with their implementation details:

Option 1: Subject Alternative Names (SAN)

openssl req -new -key domain.key \
-subj "/CN=test.com" \
-reqexts SAN \
-config <(cat /etc/ssl/openssl.cnf \
<(printf "[SAN]\nsubjectAltName=DNS:customer.com,DNS:client2.org")) \
-out domain.csr

Option 2: Reverse Proxy Configuration

Nginx example for CNAME handling:

server {
    listen 443 ssl;
    server_name customer.com;
    ssl_certificate /path/to/custom.crt;
    ssl_certificate_key /path/to/custom.key;

    location / {
        proxy_pass https://user1.test.com;
        proxy_set_header Host $host;
    }
}

Option 3: DNS-Level Solutions

For AWS Route 53 users:

resource "aws_route53_record" "alias" {
  zone_id = aws_route53_zone.primary.zone_id
  name    = "customer.com"
  type    = "A"

  alias {
    name                   = "d123.cloudfront.net"
    zone_id                = "Z2FDTNDATAQYW2"
    evaluate_target_health = false
  }
}

Important factors to evaluate:

  • Certificate validation time (DV vs OV vs EV)
  • CDN compatibility (CloudFront, Cloudflare, Fastly)
  • Automated certificate rotation processes
  • HTTP/2 and HTTP/3 implications

Sample Prometheus alert for certificate expiration:

- alert: SSLCertExpirySoon
  expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 30
  for: 5m
  labels:
    severity: critical
  annotations:
    summary: "SSL certificate will expire soon (instance {{ $labels.instance }})"
    description: "SSL certificate for {{ $labels.instance }} expires in {{ $value | humanizeDuration }}"