Tutorial

Wildcard vs SAN Certificates: A Decision Guide for Engineers Who've Been Burned by Both

May 8, 202612 min readCertPulse Engineering

Most teams pick a wildcard certificate the same way they pick coffee: whatever the team running the infrastructure happened to grab first. Then someone leaks the key, and you discover that one .pem file was authoritative for 200 subdomains, including the prod admin panel that was supposed to be on a separate trust boundary. The flip side is just as ugly. Teams that swore off wildcards end up with a 90-entry SAN certificate that nobody can renew without breaking three services and tripping a rate limit at Let's Encrypt.

This is a blast-radius decision, not a cost decision. If you're still framing wildcard vs SAN as "save $200/year on certs," you haven't been on the wrong side of a key compromise yet. I have. We're going to walk through what actually breaks, where the thresholds sit, and what mature teams settle on after they've been burned by both.

The Question Nobody Asks Until It's Too Late

The wildcard vs SAN choice is a blast-radius question, not a cost question: how much damage can one stolen private key do? Wildcards trade convenience for concentrated risk. SAN certs trade key isolation for operational complexity. After auditing dozens of inherited TLS environments, roughly 60% turn up at least one wildcard certificate whose private key location nobody on the current team can identify.

I've watched the realization hit twice. In one postmortem, a wildcard for *.example.com issued in 2019 had been distributed to every load balancer, every dev box, and every Lambda layer that needed to terminate TLS. It was still in use across 187 distinct hostnames when an old developer laptop turned up on eBay with the unencrypted key on disk. The other case was a security team that banned wildcards entirely. Six months in, the platform team was issuing new SAN certs every Tuesday with 40+ entries each, hitting Let's Encrypt rate limits, and rotating the entire fleet for a single subdomain change.

Both teams were wrong, in opposite directions. They picked their cert strategy without asking the only question that matters: if this private key leaks tomorrow, what's the recovery cost?

What Each Certificate Type Actually Is (At the X.509 Level)

A wildcard certificate is a regular X.509 cert with an asterisk label in the subjectAltName extension. A SAN certificate is the same X.509 structure with multiple explicit DNS names in subjectAltName. There is no separate cert type, no special CA flag, no different OID. The wildcard is a matching rule, governed by RFC 6125, that says one DNS label can be replaced by anything that doesn't contain a dot.

Run openssl x509 -text on a wildcard issued for *.example.com and you'll see:

X509v3 Subject Alternative Name:
    DNS:*.example.com, DNS:example.com

A SAN certificate, sometimes called a multi-domain certificate, looks like this:

X509v3 Subject Alternative Name:
    DNS:example.com, DNS:www.example.com,
    DNS:api.example.com, DNS:admin.example.com,
    DNS:metrics.example.com

Three rules that catch people:

  • Single-label match only. *.example.com matches api.example.com but not v2.api.example.com. You need *.api.example.com for the second level, and that's a separate cert.
  • No partial-label matching. foo*.example.com worked in some clients years ago. Chrome, Firefox, and Safari rejected them long since. RFC 6125 §6.4.3 calls them out explicitly.
  • CN field is deprecated for hostname verification. Modern clients (Chrome 58+, every recent Go and Python TLS stack) only check subjectAltName. A hostname in CN but not SAN fails.

The takeaway: there's no architectural difference at the certificate level. The asymmetry is operational. One cert covers a wide pattern with one key. The other covers a list of explicit names with one key.

The Blast Radius Test: When a Wildcard Certificate Becomes a Liability

A wildcard certificate's blast radius equals every subdomain matching the pattern, every host the cert was deployed to, and every system holding the private key. If any one is compromised, all are. Revocation is the only recovery path, and revocation is slower and less reliable than anyone wants to admit.

Concrete scenario. You've got *.example.com on 14 load balancers, three Kubernetes clusters, two CDN edges, and a developer's laptop they use for local TLS testing. The laptop is stolen at a conference. Recovery looks like this:

  1. Revoke the cert through your CA's API or ACME revoke endpoint.
  2. Reissue a new cert with a new key.
  3. Distribute to all 14 LBs, three clusters, two CDNs, every cached deployment.
  4. Reload every TLS-terminating service.
  5. Push an OCSP must-staple update if you had it configured (most teams don't, see why OCSP stapling is probably broken on half your endpoints).
  6. Wait 24-72 hours for CRL/OCSP propagation.

In my experience, realistic downtime if you're not already automated runs 4-12 hours of partial outages while distribution catches up. If you've got the renewal-deployment gap problem, it can be days before you notice some endpoint is still serving the revoked cert.

CAA records help with future issuance and do nothing for current key compromise. CAA tells the CA "only this issuer can sign for my domain." It does not invalidate already-issued certs.

The way to scope blast radius without giving up wildcards: separate wildcards per environment trust boundary. *.prod.example.com, *.staging.example.com, *.dev.example.com. Three keys, three blast radii, none overlapping. A staging key compromise doesn't touch prod.

The Decision Matrix: Six Questions That Actually Matter

The right answer depends on six measurable inputs, not gut feel. Apply them in order. The first that gives a clear answer is usually right.

Question Threshold Winner
Subdomain count <5 stable SAN
Subdomain count >15 in same trust zone Wildcard
Lifecycle stability Ephemeral (PR previews) Wildcard
Trust boundary Same team, same data, same compliance Single wildcard
Trust boundary Mixed ownership/scope Separate certs
ACME challenge DNS-01 unavailable SAN (HTTP-01)
Automation maturity DNS API write trust Wildcard
CT log exposure Hide internal naming Wildcard

Detail on each:

  • Subdomain count. Under 5 stable subdomains, SAN wins on operational simplicity. Over 15 stable subdomains in the same trust zone, wildcard wins on renewal cost.
  • Lifecycle stability. If subdomains come and go (PR preview environments, ephemeral staging slots), wildcards win. You can't add a SAN entry without reissuing. Wildcards just match.
  • Trust boundary alignment. Are these subdomains owned by the same team, holding the same data, with the same compliance scope? If yes, one wildcard. If no, separate certs even if the names look similar.
  • DNS-01 vs HTTP-01 capability. Wildcards require the DNS-01 challenge. If your DNS provider doesn't support API-based record updates, or you can't delegate _acme-challenge, wildcards become painful. SANs work fine with HTTP-01.
  • Renewal automation maturity. Wildcards demand DNS API credentials with TXT-write permissions. If you don't trust your automation with that scope, SANs are safer.
  • CT log exposure tolerance. SANs publish every subdomain to Certificate Transparency logs. Wildcards publish *.example.com, which leaks structure but not specific names. This isn't real security through obscurity, but CT log monitoring catches subdomain enumeration and the threat model differs.

Hands-On: Issuing Both With ACME (Let's Encrypt + acme.sh)

Working examples. These are commands I run, not theoretical syntax.

Wildcard via DNS-01 with Route53:

export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."

acme.sh --issue --dns dns_aws \
  -d example.com -d '*.example.com' \
  --dns-sleep 30 \
  --keylength ec-256

Required IAM: route53:ChangeResourceRecordSets and route53:GetChange on the hosted zone, plus route53:ListHostedZones. Anything less, you'll get:

Error: AccessDenied: User is not authorized to perform: route53:ChangeResourceRecordSets

The --dns-sleep flag matters. Default is 20 seconds. Route53 propagation can take 30-60 seconds across all NS, and acme.sh polls the CA, not your DNS. If the CA queries before the TXT record is visible:

DNS problem: NXDOMAIN looking up TXT for _acme-challenge.example.com

If you're using a delegation pattern (where _acme-challenge.example.com is a CNAME pointing to a record in a separate zone), forgetting the delegation gives the same error. Verify with dig +trace TXT _acme-challenge.example.com.

A 12-name SAN cert via HTTP-01:

acme.sh --issue \
  -d example.com -d www.example.com \
  -d api.example.com -d admin.example.com \
  -d metrics.example.com -d status.example.com \
  -d docs.example.com -d cdn.example.com \
  -d auth.example.com -d ws.example.com \
  -d media.example.com -d static.example.com \
  --webroot /var/www/html

Every domain needs to resolve to the host running acme.sh and serve /.well-known/acme-challenge/ from /var/www/html. One DNS misconfig, one missing alias, the whole cert fails:

Domain: api.example.com
Type:   unauthorized
Detail: Invalid response from http://api.example.com/.well-known/acme-challenge/...

For deeper coverage of these failure modes and why DNS-01 is the safer default at scale, see the ACME protocol's real-world pitfalls.

Renewal Reality: What Breaks at Scale

Wildcards have one renewal, one distribution, one rollback target. SANs have one renewal but failure modes multiply with each name. Here's what I've measured.

Let's Encrypt rate limits that bite SAN-heavy strategies:

  • 100 names per certificate (hard cap)
  • 50 certificates per registered domain per week
  • 5 duplicate certificates per week (same exact set of names)
  • 300 new orders per account per 3 hours

If you're running 80 services on per-service certs, you've already used 80 of your 50-per-week budget. You consolidate or hit the wall.

Cert size and TLS handshake impact at 80+ SAN entries:

  • A 2-name SAN cert with EC-256 keys: ~1.2KB on the wire
  • An 80-name SAN cert with the same keys: ~4.8KB
  • TLS 1.3 sends the cert in the ServerHello flight. That's an extra 3.6KB on every handshake, every connection.
  • OCSP responses also grow. A stapled response for a giant SAN cert can push the initial flight past the typical 14KB initcwnd window, adding a round trip on cold connections.

If you serve high-volume mobile traffic, that handshake bloat is measurable. After monitoring certificate performance across multiple production fleets, we observed 40-80ms added P95 connection time on a cert that grew from 12 to 64 SAN entries. Wildcards sidestep this. One cert, fixed size, predictable handshake.

The Hybrid Pattern Most Mature Teams Land On

After the burns, most mature teams converge on the same pattern: per-environment wildcards plus targeted SANs and standalone certs for high-value endpoints. A reference layout for a typical SaaS company:

Cert Type Scope
*.prod.example.com Wildcard Prod LBs only, key in prod KMS
*.staging.example.com Wildcard Staging only
*.dev.example.com Wildcard Dev only
example.com + www.example.com Small SAN Apex
admin.example.com Standalone Separate key, restricted distribution
api.example.com Standalone or wildcard Depends on compliance scope
*.preview.example.com Wildcard Ephemeral PR environments

Why the apex gets a separate cert: wildcards in TLS don't match the apex itself. *.example.com does not match example.com. You either include both in one cert (some CAs charge extra for that) or keep them separate.

Why admin gets isolation: the blast radius math says high-value endpoints should not share keys with normal services. If your admin panel is on the prod wildcard and a dev box leaks the key, your admin panel is exposed. A separate cert and key, ideally with shorter lifetime and tighter distribution, contains the damage. This is the architecture I recommend in the practitioner's guide to SSL certificate management.

What I'd Actually Pick (And When I'd Change My Mind)

My default: per-environment wildcards plus a small set of targeted SANs. Three or four wildcards covering trust boundaries, two or three small SANs for apex domains and grouped public services, separate certs for admin and high-value endpoints. This is what I run. It scales to thousands of subdomains without hitting rate limits, the renewal coordination is contained, and the blast radius is bounded by environment.

When I'd flip:

  • PCI scope. Anything in PCI-DSS scope gets its own cert and its own key, full stop. Sharing a wildcard certificate across PCI and non-PCI services puts the non-PCI services into scope. Easier to isolate at the cert layer than to argue with a QSA.
  • Customer-facing subdomains with separate ownership. If customer-foo.example.com is operated by a different team or sold as a per-tenant offering, that cert lives separately. Operational ownership and blast radius should match.
  • Regulated workloads. HIPAA, FedRAMP, anything where revocation timelines are externally mandated. Don't share keys across regulated and unregulated services.
  • High-volume mobile clients. If handshake size matters for your latency budget, one wildcard beats a giant SAN every time.

The honest tradeoff: per-environment wildcards mean you have a small number of high-value private keys. Protect them like crown jewels. Use HSM-backed storage where possible, restrict distribution, log every access. If you can't do that, fall back to many smaller SANs and accept the operational cost.

Either way, the wildcard certificate strategy needs to be a deliberate decision, not a default. CertPulse monitors TLS certificates across infrastructure because teams keep finding wildcards in places they didn't know they had them, and SAN certs that haven't been renewed since the engineer who set them up left in 2022. Knowing what you've got is step zero.

FAQ

Can a wildcard cover multiple levels of subdomain? No. *.example.com matches api.example.com but not v2.api.example.com. RFC 6125 limits the asterisk to a single DNS label. You'd need *.api.example.com (a separate cert) for the second level.

Are wildcards more expensive than SAN certs? With Let's Encrypt and most ACME-issuing CAs, both are free. Commercial CAs price wildcards higher, sometimes 5-10x a single-domain cert. The real cost difference is operational, not financial.

Do wildcards hide my subdomains from CT logs? Partially. The wildcard entry shows up as *.example.com, which doesn't reveal individual names. But if those subdomains appear in any other cert (an internal CA, a third-party service, an old SAN), they get logged. Don't treat CT obscurity as a security control.

Why does my wildcard work in Chrome but break my Go HTTP client? Usually a chain issue, not a wildcard issue. Chrome carries an intermediate cache that masks missing chain certs. Other clients don't. Include the full intermediate chain in your cert bundle.

What's the maximum number of SAN entries on one Let's Encrypt cert? 100. Hard cap. Other CAs vary: DigiCert allows up to 250, GlobalSign matches at 100. Anywhere near those numbers, reconsider whether a wildcard or several smaller certs would scale better.

This is why we built CertPulse

CertPulse connects to your AWS, Azure, and GCP accounts, enumerates every certificate, monitors your external endpoints, and watches Certificate Transparency logs. One dashboard for every cert. Alerts when auto-renewal fails. Alerts when certs approach expiry. Alerts when someone issues a cert for your domain that you didn't request.

If you're looking for complete certificate visibility without maintaining scripts, we can get you there in about 5 minutes.

Wildcard vs SAN Certificates: A Decision Guide for Engineers Who've Been Burned by Both | CertPulse