# NET::ERR_CERT_COMMON_NAME_INVALID: Fix the Name Mismatch

> Fix NET::ERR_CERT_COMMON_NAME_INVALID and related ERR_CERT errors: SAN vs CN, wildcard limits, missing SNI, expired or incomplete chains, with openssl checks.

Source: https://howhttpworks.com/debug/err-cert-common-name-invalid
Last reviewed: 2026-10-04

Error messages this page covers:
- `Your connection is not private. Attackers might be trying to steal your information from example.com (for example, passwords, messages, or credit cards). NET::ERR_CERT_COMMON_NAME_INVALID`
- `NET::ERR_CERT_AUTHORITY_INVALID`
- `NET::ERR_CERT_DATE_INVALID`
- `Warning: Potential Security Risk Ahead. SSL_ERROR_BAD_CERT_DOMAIN`
- `curl: (60) SSL: no alternative certificate subject name matches target hostname 'example.com'`
- `curl: (60) SSL: no alternative certificate subject name matches target host name 'example.com'`

> **TL;DR:** The server presented a certificate whose Subject Alternative Names do not include the hostname you typed (Chrome ignores the Common Name). Run `openssl s_client -connect HOST:443 -servername HOST` and compare the SANs with the URL. The usual causes are a missing `www` or apex name, a wildcard used one level too deep, and a server returning its default certificate because the client sent no SNI (connecting by IP).

## What it means

This is not an HTTP error. The failure happens during the TLS handshake, before any HTTP request is sent, so there is no status code, no `Server` header and nothing in your web server's access log. The browser compares the hostname in the URL with the names in the certificate (RFC 9110 section 4.3.4 defers to the [RFC 6125](https://www.rfc-editor.org/rfc/rfc6125) rules) and aborts on a mismatch:

```text
Your connection is not private
Attackers might be trying to steal your information from example.com (for example, passwords, messages, or credit cards).
NET::ERR_CERT_COMMON_NAME_INVALID
```

The same fault in other clients:

```text
Firefox:  SSL_ERROR_BAD_CERT_DOMAIN
Safari:   This Connection Is Not Private
curl:     curl: (60) SSL: no alternative certificate subject name matches target hostname 'example.com'
          (older curl: "target host name")
openssl:  Verification error: hostname mismatch
```

"Common name" refers to the legacy `CN` field in the certificate subject. Chrome has ignored it since version 58. RFC 6125 section 6.4.4 lets a client fall back to the CN only when the certificate has no DNS SAN at all, and browsers dropped even that. A certificate that says `CN = example.com` can therefore still fail: the SAN list does not contain it.

## Who sent it?

The browser decided, from the certificate it was shown. The question is which server showed a certificate for the wrong names, and the answer is whoever terminates TLS for that hostname: your web server, a load balancer, a CDN or an ingress controller. Look at the real certificate:

```bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
```

```text
subject=CN = example.org
issuer=C = US, O = Let's Encrypt, CN = R11
notBefore=Sep  1 08:00:00 2026 GMT
notAfter=Nov 30 08:00:00 2026 GMT
X509v3 Subject Alternative Name:
    DNS:example.org, DNS:www.example.org
```

Here the certificate is valid but for `example.org`, not `example.com`. Now repeat without `-servername` to see the server's default certificate:

```bash
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
```

If the two differ, the server picks the certificate by SNI (Server Name Indication, RFC 6066 section 3), and the hostname you are testing is not configured on it, so it falls through to the default. If you are connecting by IP, add `-servername` with the real hostname; browsers never send an IP address as SNI.

Identify the terminating layer with the response headers you can still get, using `-k` only for diagnosis:

```bash
curl -skI https://example.com/ | grep -iE '^(server|via|cf-ray|x-amz|x-served-by)'
```

## Fix it, in order of likelihood

1. The certificate does not list the hostname. Reissue it with every name users type, including `example.com` and `www.example.com`.
2. A wildcard is being used for the wrong level. `*.example.com` does not cover `example.com` or `a.b.example.com`.
3. The server has no certificate for that name and falls back to a default (new vhost without TLS, new domain added to a CDN or ALB without a certificate).
4. DNS points to the wrong server, for example a stale A record sending traffic to the old host that still has the old certificate.
5. An internal or private hostname uses a public certificate, or the reverse.

### Let's Encrypt with certbot

```bash
# Issue one certificate covering both names
certbot --nginx -d example.com -d www.example.com

# Add a name to an existing certificate
certbot --nginx --expand -d example.com -d www.example.com -d api.example.com

# Check what is installed
certbot certificates
```

Wildcards require the DNS-01 challenge (`certbot certonly --dns-cloudflare -d example.com -d '*.example.com'` with the matching plugin).

### nginx: serve the right certificate, and refuse unknown names

```nginx
server {
    listen 443 ssl;
    http2 on;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}

# Default server: refuse the handshake for hostnames you do not host (nginx 1.19.4+)
server {
    listen 443 ssl default_server;
    ssl_reject_handshake on;
}
```

Without a default server that rejects, nginx hands unknown names the first `server` block's certificate, which is exactly how "wrong certificate" errors reach visitors.

### Kubernetes with cert-manager

Every hostname in the Ingress needs to be listed under `tls.hosts` and in the Certificate's `dnsNames`:

```yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-com
spec:
  secretName: example-com-tls
  dnsNames:
    - example.com
    - www.example.com
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
```

### AWS ACM, ALB and CloudFront

The certificate attached to the listener or distribution must list the alternate domain name you are serving. For CloudFront, the certificate must be in `us-east-1`, and the distribution's alternate domain names (CNAMEs) must be covered by it. On an ALB listener you can attach several certificates and the ALB picks one by SNI.

## The other ERR_CERT errors

### NET::ERR_CERT_AUTHORITY_INVALID

The browser cannot chain the certificate to a trusted root. Causes, most common first:

- The server sends only the leaf certificate and not the intermediates. Many desktop browsers can fetch missing intermediates, but Android and curl often cannot, so it works for you and fails elsewhere. Serve `fullchain.pem`, not `cert.pem`.
- Self-signed or private-CA certificate that the device does not trust.
- A corporate proxy that inspects TLS and re-signs certificates with a company root the browser or OS does not have installed.

```bash
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | grep -E 'verify|depth|s:|i:'
```

Look for `verify error:num=20:unable to get local issuer certificate` or `num=21:unable to verify the first certificate`. Both point to a missing intermediate. Also check `Verify return code` at the end of the output.

### NET::ERR_CERT_DATE_INVALID

The certificate is expired or not yet valid, or the visitor's clock is wrong. Check dates server-side:

```bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates

# Exit status 0 if the cert is still valid 30 days from now, 1 if not
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -checkend $((30*24*3600))
```

If the server's dates are fine and only some devices fail, the device clock is the problem. If issuance is automated, check the renewal job (`certbot renew --dry-run`) and that nginx was reloaded after renewal, since nginx loads certificates at start or reload and does not notice a replaced file.

Cloudflare in front of your origin adds two related errors for the origin leg: 525 (TLS handshake failed) and 526 (invalid origin certificate when SSL mode is Full strict). They mean your origin's certificate has one of the problems above.

## Reproduce and verify

```bash
# Hostname verification as a client would do it (exit code 0 means OK)
curl -sS -o /dev/null -w '%{http_code} ssl_verify=%{ssl_verify_result}\n' https://example.com/

# Verify the hostname explicitly with openssl (-verify_hostname needs OpenSSL 1.1.0+)
echo | openssl s_client -connect example.com:443 -servername example.com -verify_hostname example.com 2>/dev/null \
  | grep -E 'Verification|Verify return code'
```

A `ssl_verify` of `0` and `Verify return code: 0 (ok)` mean the chain and the hostname match. Test every name (apex, `www`, API subdomain) and also from a second network, because a split-horizon DNS or a CDN edge can serve a different certificate than the one you tested from.

## Related

- [HTTPS and TLS](https://howhttpworks.com/guides/https-and-tls) explains the handshake, chains of trust and what a certificate vouches for.
- [Strict-Transport-Security](https://howhttpworks.com/headers/strict-transport-security) removes the click-through option for certificate errors on hosts that sent it.
- [Mixed content](https://howhttpworks.com/debug/mixed-content-blocked) is the failure you meet next when the certificate works but the page still loads HTTP resources.
- [Too many redirects](https://howhttpworks.com/debug/err-too-many-redirects) often appears together with TLS offload misconfiguration.
- [ERR_SSL_PROTOCOL_ERROR](https://howhttpworks.com/debug/err-ssl-protocol-error) is the failure one step earlier, when the handshake breaks before any certificate is checked.
- [ERR_CERT_DATE_INVALID](https://howhttpworks.com/debug/err-cert-date-invalid): expired certificates, renewals that never got deployed, expired intermediates and wrong client clocks.
