Fix NET::ERR_CERT_COMMON_NAME_INVALID โ€” SSL Certificate Name Mismatch Error

intermediate๐Ÿ”’ SSL/TLS2026-03-21| Chrome, Firefox, Edge, Safari โ€” any browser; affects all OS (Windows, macOS, Linux); web servers: Nginx, Apache, Caddy; any domain with TLS/SSL configured

Error Message

NET::ERR_CERT_COMMON_NAME_INVALID โ€” The server's security certificate does not match the website's URL.
#ssl#certificate#common-name#san#domain

The Error

You hit this in the browser:

NET::ERR_CERT_COMMON_NAME_INVALID
The server's security certificate does not match the website's URL.

The certificate on your server doesn't cover the domain the browser is trying to reach. The browser refuses to connect.

Root Cause

TLS certificates declare which domains they cover in two fields: the Common Name (CN) and the Subject Alternative Names (SAN) extension. Modern browsers only check SANs. Chrome dropped CN-only matching in Chrome 58 (2017), and every other major browser followed.

The usual culprits:

  • Certificate issued for example.com but you're visiting www.example.com (or vice versa)
  • Certificate issued for one domain, deployed on a server hosting a different domain
  • Accessing a server by IP address when the cert only lists a hostname
  • Wildcard cert *.example.com โ€” covers subdomains but NOT bare example.com
  • Self-signed cert generated without a SAN extension
  • Stale cert file from a previous domain still configured on the server

Step 1: Diagnose the Mismatch

Before touching anything, find out exactly what domains the deployed cert actually covers:

# Check which names the cert lists in its SAN field
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"

# Also check the CN
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -subject

A mismatched cert looks like this:

Subject: CN = old-domain.com
X509v3 Subject Alternative Name:
    DNS:old-domain.com, DNS:www.old-domain.com

If your domain isn't listed under SAN, that's your problem.

Fix 1: Reissue the Certificate with the Correct Domains

The right long-term fix: get a new cert that actually covers your domain. Use Let's Encrypt with Certbot:

# Single domain + www
certbot --nginx -d example.com -d www.example.com

# Or with Apache
certbot --apache -d example.com -d www.example.com

# Standalone mode (no web server integration)
certbot certonly --standalone -d example.com -d www.example.com

Certbot automatically adds both domains to the SAN field. Reload your web server after:

sudo systemctl reload nginx
# or
sudo systemctl reload apache2

Fix 2: Right Cert, Wrong Path โ€” Point the Server to the Correct File

Sometimes the correct cert already exists on the server โ€” it's just not the file your config is loading. Check and fix the paths.

Nginx:

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    # Make sure these point to the cert covering example.com
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}

Apache:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>

Test the config before reloading โ€” never skip this step:

sudo nginx -t && sudo systemctl reload nginx
# or
sudo apachectl configtest && sudo systemctl reload apache2

Fix 3: Self-Signed Cert Missing SAN (Dev/Internal)

A plain openssl req command generates a cert with CN only โ€” no SAN extension. Chrome rejects these outright. You need to pass a config file that explicitly includes SAN entries:

# Create an OpenSSL config with SAN
cat > cert.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
CN = myapp.local

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = myapp.local
DNS.2 = localhost
IP.1  = 127.0.0.1
EOF

# Generate cert with SAN included
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout myapp.key -out myapp.crt -config cert.cnf

Then trust it locally. On macOS:

sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain myapp.crt

On Ubuntu/Debian:

sudo cp myapp.crt /usr/local/share/ca-certificates/myapp.crt
sudo update-ca-certificates

Fix 4: Wildcard Cert Doesn't Cover the Root Domain

*.example.com covers www.example.com, api.example.com, and any other subdomain โ€” but bare example.com is not included. Reissue with both explicitly listed:

certbot certonly --nginx -d example.com -d '*.example.com' \
  --preferred-challenges dns-01

Wildcard certs require the DNS-01 challenge instead of HTTP-01. During issuance, Certbot will ask you to add a _acme-challenge TXT record to your DNS zone. Add it, wait 30โ€“60 seconds for propagation, then continue.

Verify the Fix

After any change, confirm the cert now lists the right domains before declaring victory:

# Check SAN entries on live server
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -text | grep -A5 "Subject Alternative Name"

# Expected output:
# X509v3 Subject Alternative Name:
#     DNS:example.com, DNS:www.example.com

# Quick validity check
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates

Cross-check with curl โ€” a valid cert returns a normal response header, no SSL warnings:

curl -vI https://example.com 2>&1 | grep -E "SSL|subject|issuer|expire"

No SSL certificate problem in the output? You're done.

Prevention

  • Always include both bare domain and www in every cert request, even when you redirect one to the other. Takes five seconds to add -d www.example.com; takes much longer to debug later.
  • Set up auto-renewal. Test it first with certbot renew --dry-run, then rely on the systemd timer Certbot installs at /etc/systemd/system/snap.certbot.renew.timer (or add your own cron). Let's Encrypt certs expire in 90 days โ€” don't manage this manually.
  • Test certs before deploying with openssl verify -CAfile chain.pem cert.pem or run your domain through SSL Labs (ssllabs.com/ssltest) for a full report including SAN coverage.
  • For internal services, use a proper internal CA โ€” tools like step-ca or cfssl make it straightforward. Ad-hoc self-signed certs are fine for one machine; they become a maintenance nightmare across a team.

Related Error Notes