# Node.js ERR_TLS_CERT_ALTNAME_INVALID: hostname mismatch fixed

> ERR_TLS_CERT_ALTNAME_INVALID means the certificate does not list the hostname or IP you connected to. Fix the name, SNI or certificate.

Source: https://ssl.sh/errors/node-err-tls-cert-altname-invalid/
Last updated: 2026-11-10

**`ERR_TLS_CERT_ALTNAME_INVALID` means the name or IP address your Node.js code connected to is not listed in the server certificate's Subject Alternative Name extension.** The certificate may be trusted and valid; it belongs to a different name. Fix the URL, the SNI name or the certificate. Do not disable verification.

## What the error means

The chain is valid, but the certificate does not list the host you connected to. After the chain is verified, Node.js calls `tls.checkServerIdentity(hostname, cert)` to compare the requested host with the certificate. When nothing matches, it throws an error with `code: 'ERR_TLS_CERT_ALTNAME_INVALID'`, extra `reason`, `host` and `cert` properties, and a message such as:

```text
Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: Host: example.com. is not in the cert's altnames: DNS:www.example.com
```

The part after the colon is the `reason`. These are the variants Node.js produces:

- `Host: <name>. is not in the cert's altnames: DNS:...`: the DNS names in the certificate do not cover the host.
- `IP: <address> is not in the cert's list: ...`: you connected to an IP address that is not an IP SAN.
- `Host: <name>. is not cert's CN: ...`: the certificate has no DNS SANs, so Node.js fell back to the Common Name, which also did not match.
- `Cert does not contain a DNS name`: neither DNS SANs nor a Common Name to compare.

The browser equivalent is [NET::ERR_CERT_COMMON_NAME_INVALID](https://ssl.sh/errors/net-err-cert-common-name-invalid/); the logic and the fixes are the same.

## Causes, most common first

### 1. The URL uses a name the certificate does not cover

The classic case is `https://example.com` against a certificate issued only for `www.example.com`, or a staging host served with the production certificate. Wildcards add another trap: `*.example.com` covers exactly one label, so it matches neither `example.com` nor `a.b.example.com`.

- **Confirm:** list the names in the certificate the server presents:

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

Or paste the certificate into the [certificate decoder](https://ssl.sh/tools/certificate-decoder/).

- **Fix:** use a covered name, or reissue the certificate with every name you serve, for example `certbot certonly -d example.com -d www.example.com`.

### 2. The server returns its default certificate because of SNI

Servers with many sites pick the certificate by the SNI name in the ClientHello. When the client connects by IP, through a tunnel, or with a custom DNS lookup, and does not send the right SNI, it gets the default site's certificate.

- **Confirm:** compare the certificate with and without `-servername` in `openssl s_client`; different subjects mean SNI decides.
- **Fix:** pass the real name explicitly:

```javascript
const tls = require('node:tls');
const socket = tls.connect({ host: '203.0.113.10', port: 443, servername: 'example.com' }, () => {
  console.log('authorized:', socket.authorized);
  socket.end();
});
```

### 3. Connecting by IP address

`https://203.0.113.10/` fails unless the certificate has that IP as an IP SAN. Internal services reached by IP, Docker or Kubernetes service names (`https://api:8443`) and `localhost` hit this often.

- **Fix:** call the service by a DNS name that is on the certificate, or issue the internal certificate with the IP or service name in its SAN list.

### 4. A CDN, load balancer or hosting platform serves the wrong certificate

A custom domain pointed at a CDN, object storage bucket or PaaS before its certificate was uploaded returns the platform's own certificate, whose SANs are the platform's domains.

- **Confirm:** the SAN list shows the provider's domains instead of yours.
- **Fix:** upload or provision the certificate for your domain in the provider's console, then retest.

### 5. The certificate was not updated after adding a domain

The server still serves an older certificate that predates the new name. Renew or reissue with the full name list and reload the server.

## Fix commands by platform

Every fix makes the host name the client checks match a SAN in the certificate the server sends.

**Linux / macOS (check what the server sends):**

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

**Windows PowerShell (same check with Node.js):**

```powershell
node -e "const s=require('tls').connect(443,'example.com',{servername:'example.com'},()=>{console.log(s.getPeerCertificate().subjectaltname);s.end()})"
```

**Calling an IP but verifying the real name** (keeps full verification):

```javascript
const https = require('node:https');
const tls = require('node:tls');
https.get({
  host: '203.0.113.10',
  servername: 'internal.example.com',
  headers: { Host: 'internal.example.com' },
  checkServerIdentity: (_host, cert) => tls.checkServerIdentity('internal.example.com', cert),
}, res => console.log(res.statusCode));
```

**Nginx serving several sites:** make sure each `server` block has the right `server_name` and its own `ssl_certificate`, and that the default server does not catch your domain.

## Don't disable certificate verification

Each switch that hides this error removes protection against impersonation. `rejectUnauthorized: false`, `NODE_TLS_REJECT_UNAUTHORIZED=0` and `checkServerIdentity: () => undefined` all make the error go away. The first two accept any certificate at all; the third keeps chain validation but drops the name check, which is equally bad: any attacker who owns any trusted certificate for any domain can then impersonate your server and read tokens, passwords and API responses.

The safe alternatives are specific: connect with the right name, set `servername` so SNI selects the right certificate, issue certificates with every name you use, and when you must dial an IP, verify against the expected hostname with `tls.checkServerIdentity(expectedName, cert)` as shown above.

## Verify the fix

```bash
node -e "require('https').get('https://example.com', r => { console.log(r.statusCode, r.socket.authorized); r.resume(); }).on('error', e => console.error(e.code, e.reason || e.message))"
```

It should print a status code and `true`. Also run both the bare and `www` names through the [SSL checker](https://ssl.sh/tools/ssl-checker/) to confirm each one matches, and read about [Subject Alternative Names](https://ssl.sh/glossary/san/) if you are planning which names to put on a new certificate.
