If you spend enough time around certificates, you eventually run into a hostname mismatch that looks absurdly avoidable. The certificate is valid, signed by a trusted CA, not expired, and still the browser rejects it because the hostname is not covered the way someone assumed.
Most of those incidents come back to SANs.
Subject Alternative Names are one of the most important parts of a modern TLS certificate, but they are often treated as clerical detail instead of a design decision. I have seen SAN mistakes break renewals, confuse wildcard coverage, derail load balancer migrations, and cause private PKI enrollments to fail in ways that only show up after deployment.
This guide explains what SANs are, why Common Name matching is no longer enough, how to include SANs in CSRs with OpenSSL, how to inspect them on live certificates, and where the practical limits start to matter.
What SANs are
A Subject Alternative Name is an X.509 extension that lists identities a certificate is valid for.
Most commonly, that means DNS names such as:
example.comwww.example.comapi.example.com
But SANs can also contain:
- IP addresses
- email addresses
- URIs
- other identity forms depending on the certificate use case
For web TLS, the important type is usually DNS and sometimes IP.
Example SAN section from a certificate:
X509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.com, DNS:api.example.com
If the hostname a client is connecting to is not covered by the SAN extension, the certificate should be rejected even if the Common Name looks close.
Why Common Name is deprecated for hostname validation
Historically, many clients matched hostnames against the certificate Common Name (CN). That old behavior is why people still say things like “the CN on the cert is correct” as though that ends the discussion.
It does not.
Per modern practice and long-standing RFC guidance, hostname validation should use SANs. RFC 2818 pushed this direction years ago, and browser behavior eventually converged on it. Chrome and other modern clients stopped treating Common Name-only certificates as sufficient for hostname validation.
That means a certificate like this is no longer good enough for normal browser use:
Subject: CN=example.com
if it has no SAN extension.
I still run into internal PKI setups where someone issues CN-only certs because “that used to work.” In modern client environments, that is deprecated behavior and should be retired.
Browsers rejecting certificates without SANs
Modern browsers expect SANs.
If you deploy a certificate with a matching CN but no SAN extension, browsers may reject it with a hostname-related error. This surprises people who learned certificate handling a decade ago and have not revisited the topic since.
The practical rule is simple:
- for server certificates, always include SANs
- do not rely on Common Name matching
This matters for both public CA issuance and internal/private PKI.
If you want the wider naming context, this guide on wildcard vs multi-domain SAN certificates complements the topic well.
Reading SANs from an existing certificate
This is one of the first checks I do during hostname mismatch troubleshooting.
For a local certificate file:
openssl x509 -in server.crt -noout -text
To filter directly to the SAN section:
openssl x509 -in server.crt -noout -text | grep -A1 "Subject Alternative Name"
For a live endpoint:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
That last command tells you what the server is actually presenting, which is more useful than inspecting a file you hope is deployed.
If you rely on openssl s_client often, this practical s_client guide is a good companion.
Adding SANs to a CSR with OpenSSL
This is where many mistakes happen.
You can no longer just run a one-line CSR command with a CN and assume the result will be right. You need SANs explicitly.
Method 1: use an OpenSSL config file
Create a config like this:
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
CN = example.com
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.com
Then generate the key and CSR:
openssl req -new -nodes \
-newkey rsa:2048 \
-keyout example.com.key \
-out example.com.csr \
-config example.com.cnf
Verify the CSR contains the expected SANs:
openssl req -in example.com.csr -noout -text | grep -A1 "Subject Alternative Name"
I always check the CSR before sending it anywhere. It is cheaper to catch SAN mistakes there than after issuance and deployment.
Method 2: use -addext
On newer OpenSSL versions, this is often the quickest option:
openssl req -new -nodes \
-newkey rsa:2048 \
-keyout example.com.key \
-out example.com.csr \
-subj "/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com,DNS:api.example.com"
Again, verify afterward:
openssl req -in example.com.csr -noout -text | grep -A1 "Subject Alternative Name"
This is convenient, but for larger private PKI workflows I still prefer config files because they are easier to review and reproduce.
For a fuller CSR workflow, this CSR generation guide covers the surrounding best practices.
Wildcards vs SANs: the trade-offs
A wildcard certificate and a multi-SAN certificate solve similar problems differently.
Wildcard certificates
A wildcard like *.example.com covers one label level beneath the domain, such as:
app.example.comapi.example.comcdn.example.com
It does not cover:
example.coma.b.example.com
Wildcard certs are convenient, especially in dynamic environments, but they are broad.
SAN certificates
A SAN certificate explicitly lists the hostnames it covers.
That is more verbose, but also more precise. I prefer SAN certificates when I know exactly which names should exist and I want the certificate scope to reflect that.
Trade-offs I have noticed in practice:
- SAN certs are more explicit and easier to audit
- wildcards are simpler when many first-level subdomains are short-lived
- wider certificates increase blast radius if the private key is exposed
- operational ownership matters: one wildcard shared across teams can become a governance mess
If you are choosing between them, this wildcard vs SAN article goes deeper.
SANs in multi-domain certificates
A SAN certificate can cover multiple unrelated hostnames, for example:
example.comwww.example.comexample.netapi.example.org
That is what people typically mean by a multi-domain or SAN certificate.
It can be useful on shared appliances, edge proxies, and environments where one endpoint legitimately serves several names. But do not overdo it.
The downsides are real:
- every reissue touches all covered names
- one hostname addition changes the whole certificate
- larger SAN lists make management and review harder
- wider coverage increases impact if the key leaks
I prefer smaller blast-radius certificates unless the consolidation benefit is clear.
Let’s Encrypt SAN support and limits
Let’s Encrypt supports SAN certificates and makes them easy to request in many clients.
For example, with Certbot:
sudo certbot certonly --nginx \
-d example.com \
-d www.example.com \
-d api.example.com
One practical limit that matters: Let’s Encrypt allows up to 100 SANs per certificate.
That sounds generous, but it is not a recommendation to pack 100 names into one cert. Large SAN lists become harder to reason about and more expensive to rotate operationally.
I have seen teams treat a SAN certificate like a junk drawer because it was convenient in the moment. Six months later, nobody wanted to touch renewal because no one was sure which services depended on which names.
If you are working with Let’s Encrypt generally, this Certbot guide covers the automation side.
SANs for internal domains and private PKI
SAN rules matter just as much inside private PKI as they do on the public internet.
Common internal uses include:
- internal API hostnames
- service discovery names
- VPN or appliance management FQDNs
- mTLS server identities
- internal load balancer names
The main pitfall I still see is internal CA tooling issuing CN-only certs because nobody updated the template. Modern clients and libraries increasingly expect SANs there too.
For private PKI, I prefer explicit SAN templates and enrollment profiles rather than leaving CSR extensions to chance. If you build your own CA, this OpenSSL CA guide is a good baseline.
SANs in self-signed and lab certificates
This catches people constantly in development and lab environments. Someone creates a self-signed certificate with only a Common Name, tests it in an old tool that accepts it, and assumes the result is representative. Then Chrome, curl with strict verification, a Java client, or a reverse proxy rejects it because the SAN extension is missing.
If you build self-signed certs for internal testing, include SANs exactly the same way you would for a CA-signed CSR. The fact that the certificate is self-signed does not change modern hostname validation rules.
A minimal OpenSSL command using -addext looks like this:
openssl req -x509 -newkey rsa:2048 -nodes -days 30 \
-keyout lab.key \
-out lab.crt \
-subj \"/CN=lab.example.internal\" \
-addext \"subjectAltName=DNS:lab.example.internal,IP:192.0.2.50\"
Then verify it:
openssl x509 -in lab.crt -noout -text | grep -A1 "Subject Alternative Name"
I prefer doing this even for throwaway lab certificates, because it keeps test behavior closer to production and avoids a whole class of “works in dev, fails in prod” certificate surprises.
IP address SANs
If clients connect directly to an IP address and expect certificate validation to succeed, you need an IP SAN, not just a DNS SAN or CN.
Example config snippet:
[ alt_names ]
DNS.1 = example.com
IP.1 = 203.0.113.10
IP.2 = 2001:db8::10
Generating the CSR works the same way as before.
This matters for:
- appliance interfaces
- test environments
- load balancer direct probes
- private infrastructure without stable DNS
One important point: an IP address in the CN is not a substitute for an IP SAN in modern hostname validation.
Common mistakes with SANs
Assuming *.example.com covers example.com
It does not. If you need the apex name too, include it explicitly as a SAN.
Forgetting new service hostnames during migration
This happens during blue/green or reverse proxy changes. The old cert covers www, the new path needs api, and the omission only appears at cutover time.
Reusing giant SAN certs everywhere
Convenient at first, painful later.
Not verifying the CSR before issuance
If the SANs are wrong in the CSR, the issued certificate will be wrong too.
Assuming browsers still accept CN-only certificates
That is outdated behavior.
Practical limits on SAN count
There is no magic number where SANs become “bad,” but there are practical thresholds.
As the SAN list grows:
- certificate management becomes less clear
- issuance and renewal workflows become more coupled
- debugging hostname coverage gets slower
- accidental over-broad trust becomes easier
Even below hard CA limits, I try to keep SAN lists focused. If the certificate starts representing many unrelated applications or teams, that is usually a sign to split it.
Renewal planning with SAN-heavy certificates
Another operational issue with SANs is renewal coordination. A single certificate that covers many hostnames often has many owners in practice, even if only one team requested it originally. Months later, nobody is quite sure whether old-admin.example.com still matters, whether api2.example.com should be added, or whether one business unit is about to remove a name that another team still depends on.
That is why I like keeping an inventory next to the certificate request process:
- which SANs are included
- why each name is there
- who owns each service name
- whether the certificate is public-facing, internal-only, or used for mTLS
Without that inventory, renewals become archaeology. Someone asks for “the same cert as last time,” and the SAN list keeps growing because removing anything feels risky. That is how certificates turn into bloated shared dependencies instead of scoped identity documents.
When I review a SAN list before renewal, I ask two questions:
- does each hostname still exist and need this specific certificate?
- would splitting this into two or three smaller certificates reduce blast radius and simplify ownership?
Those questions are not glamorous, but they prevent a lot of unnecessary certificate sprawl.
They also make audits easier. When someone asks why a certificate contains fifteen names, “because it has always been that way” is not a satisfying answer.
Troubleshooting hostname mismatch errors
When a client says the hostname does not match, I run through a simple process.
1. Inspect the live SANs
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
2. Confirm the exact hostname the client used
This sounds obvious, but I have seen failures caused by one missing subdomain label, a direct-IP connection, or a forgotten internal alias.
3. Check whether SNI or the wrong vhost is involved
If the server presented the wrong certificate entirely, the SANs may be fine for a different host.
4. Verify wildcard logic carefully
*.example.com covers app.example.com, not deep.app.example.com and not example.com.
5. Review the CSR and issuance request if you generated the cert yourself
That tells you whether the problem started at request time or deployment time.
Deprecated practices vs current recommendations
Deprecated: relying on Common Name for hostname validation
Do not build new processes around CN-only certs.
Deprecated: one-line CSR generation without SAN planning
That used to work for some environments. It is not enough now.
Current recommendation: treat SAN design as part of service design
Decide which names should be covered, who owns them, whether wildcard scope is appropriate, and how renewal will be automated.
Conclusion
SANs are not a minor certificate detail. They are the primary way modern clients decide whether a certificate is valid for a hostname. Common Name matching is effectively legacy behavior, and certificates without SANs are not something I trust for modern deployments.
The practical habits are straightforward: always include SANs in CSRs, verify them before issuance, inspect live certificates with OpenSSL, and be deliberate about wildcard versus explicit SAN coverage. Smaller, well-scoped certificates are easier to manage than giant multi-purpose ones. In my experience, SAN problems are usually preventable; they only become painful when teams treat certificate naming as an afterthought.