When working with wildcard SSL certificates, a common pitfall catches many developers off guard: while *.domain.com
covers all subdomains like api.domain.com
and blog.domain.com
, it explicitly does not include the root domain domain.com
. This creates security warnings and connection issues when users access your base domain.
The technical specification (RFC 2818) treats wildcard certificates differently from regular certificates. The asterisk only replaces one subdomain level, and certificate authorities explicitly exclude root domains from wildcard coverage for security reasons.
Here's what Chrome shows when you only have a wildcard cert:
SSL_ERROR_BAD_CERT_DOMAIN Subject Alternative Name does not include domain.com
Option 1: SAN (Subject Alternative Name) Certificate
Request a certificate with both entries:
openssl req -new -key domain.key -out domain.csr \ -subj "/CN=*.domain.com" \ -reqexts SAN \ -config <(cat /etc/ssl/openssl.cnf \ <(printf "[SAN]\nsubjectAltName=DNS:domain.com,DNS:*.domain.com"))
Option 2: Multi-Domain Wildcard Certificate
Some CAs offer certificates covering:
*.domain.com domain.com *.sub.domain.com sub.domain.com
Example from Let's Encrypt using certbot:
certbot certonly --manual --preferred-challenges=dns \ -d domain.com -d *.domain.com
Option 3: Separate Certificates
For high-security environments, maintain separate certificates:
# Nginx configuration example server { listen 443 ssl; server_name domain.com; ssl_certificate /path/to/root_cert.pem; ssl_certificate_key /path/to/root_key.pem; # ... other config } server { listen 443 ssl; server_name *.domain.com; ssl_certificate /path/to/wildcard_cert.pem; ssl_certificate_key /path/to/wildcard_key.pem; # ... other config }
Testing across browsers reveals different behaviors:
- Chrome 85+: Strict SAN checking
- Firefox 80+: Follows RFC strictly
- Safari 14: Partial wildcard support
For Let's Encrypt users, this bash script automates renewal:
#!/bin/bash DOMAIN="domain.com" EMAIL="admin@domain.com" certbot certonly --dns-route53 -d "$DOMAIN" -d "*.$DOMAIN" \ --non-interactive --agree-tos --email "$EMAIL" \ --preferred-challenges dns-01 # Combine certificates for HAProxy cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem \ /etc/letsencrypt/live/$DOMAIN/privkey.pem \ > /etc/haproxy/certs/$DOMAIN.pem
When you generate a wildcard SSL certificate using *.domain.com
, it automatically covers all first-level subdomains like:
https://mail.domain.com https://api.domain.com https://blog.domain.com
But surprisingly, it doesn't include the root domain (domain.com
). This creates a security gap that many developers discover only during production deployment.
The solution lies in properly configuring Subject Alternative Names (SANs) during certificate generation. Here's what happens at the protocol level:
Certificate Field: - Common Name: *.domain.com - SANs: [domain.com, *.domain.com]
Modern browsers actually ignore the Common Name field and rely entirely on SANs for validation since Chrome 58 (2017).
Here's how to implement this correctly with different tools:
OpenSSL Configuration
[req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name] C = US ST = California L = San Francisco O = Your Company OU = IT CN = *.domain.com [v3_req] keyUsage = keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = domain.com DNS.2 = *.domain.com
Certbot (Let's Encrypt) Example
certbot certonly --manual \ --preferred-challenges=dns \ --agree-tos \ -m admin@domain.com \ -d domain.com \ -d *.domain.com \ --server https://acme-v02.api.letsencrypt.org/directory
AWS Certificate Manager
When requesting a certificate in AWS:
aws acm request-certificate \ --domain-name "domain.com" \ --subject-alternative-names "*.domain.com" \ --validation-method DNS
Azure App Service
For Azure's wildcard certificates, you need to:
- Upload a PEM file containing both names
- Ensure the certificate's "Hostname" field includes both entries
After implementation, verify with OpenSSL:
openssl x509 -in certificate.crt -text -noout | grep -A 1 "Subject Alternative Name"
Should output:
DNS:domain.com, DNS:*.domain.com
- Never assume wildcards include root domains
- Check certificate expiration dates (wildcards often have shorter validity)
- Remember CDN configurations may need separate certificates
For complex environments needing *.*.domain.com
coverage:
[alt_names] DNS.1 = domain.com DNS.2 = *.domain.com DNS.3 = *.*.domain.com
Note: Some CAs restrict multi-level wildcards due to security concerns.