How to fix Error: Server name indication (SNI) name mismatch (certificate doesn't match hostname) in Node.js

Node.jsADVANCEDHIGHUpdated Dec 9, 2025· 164 views

This error occurs when the hostname sent during TLS handshake (Server Name Indication) does not match the hostname on the SSL/TLS certificate. The server receives a different SNI name than what the certificate is valid for, causing verification to fail.

Quick fix

Fixes ERR_TLS_SNI_NAME_MISMATCH

const https = require('https');

// Using tls.connect()
const socket = require('tls').connect({
  host: '192.168.1.100',        // Can be IP address
  port: 443,
  servername: 'api.example.com', // Must match certificate CN or SAN
  ca: [certificateBuffer]
});

socket.on('connect', () => {
  console.log('Connected successfully');
  socket.end();
});

socket.on('error', (err) => {
  console.error('TLS error:', err.message);
});
  1. Explicitly set the servername option in your TLS connection
  2. Use an HTTP Agent with proper TLS options
  3. Verify the certificate's Subject Alternative Names (SANs)
  4. When connecting to IP addresses, use the correct hostname
node.js — error
const https = require('https');
// Using tls.connect()
const socket = require('tls').connect({
host: '192.168.1.100', // Can be IP address
port: 443,
servername: 'api.example.com', // Must match certificate CN or SAN
ca: [certificateBuffer]
});
socket.on('connect', () => {
console.log('Connected successfully');
socket.end();
});
socket.on('error', (err) => {
console.error('TLS error:', err.message);
});
Error: Server name indication (SNI) name mismatch (certificate doesn't match hostname)

What this error means

The TLS SNI (Server Name Indication) mismatch error happens when the server name sent to the TLS server during the handshake doesn't match the certificate's Subject Alternative Names (SANs) or Common Name (CN). SNI is an extension to the TLS protocol that allows clients to specify which hostname they're connecting to before the server presents its certificate. This is critical for servers hosting multiple TLS certificates (one per domain). When Node.js makes an HTTPS request, it automatically sends the target hostname as the SNI value. If this SNI name doesn't match what the certificate is valid for, the TLS handshake fails. This error differs from a generic certificate validation failure. The certificate itself may be valid (not expired, properly signed), but the hostname it covers doesn't match the one being requested.

Typical symptoms

  • HTTPS requests fail with "SNI name mismatch" error during TLS handshake
  • Making requests to IP addresses instead of hostnames causes certificate validation failures
  • Proxying requests between domains fails without proper SNI configuration
  • Using custom host headers without updating SNI causes connection failures
  • API calls with mismatched Host header and actual target hostname fail
  • Requests work in some HTTP clients (like curl with --servername) but fail in Node.js
  • HTTPS connections succeed to domain names but fail to IP addresses

Common causes

  1. Connecting to an IP address when certificate is issued for a specific hostname
  2. Using a Host header that differs from the actual target hostname without explicitly setting SNI
  3. Proxying HTTPS requests with request Host headers that don't match the upstream certificate
  4. Making HTTPS requests through intermediate proxies that rewrite the Host header
  5. Not specifying the servername option when connecting to TLS server
  6. HTTP Agent using the Host header for SNI without validation
  7. Self-signed or internal certificates with incorrect Subject Alternative Names
  8. Connecting to localhost with a certificate issued for a different domain name

How to fix "Error: Server name indication (SNI) name mismatch (certificate doesn't match hostname)"

1Explicitly set the servername option in your TLS connection

When making HTTPS requests with the https module or a client library, always specify the servername option to ensure it matches the certificate's hostname:

javascript
const https = require('https');

// Using tls.connect()
const socket = require('tls').connect({
  host: '192.168.1.100',        // Can be IP address
  port: 443,
  servername: 'api.example.com', // Must match certificate CN or SAN
  ca: [certificateBuffer]
});

socket.on('connect', () => {
  console.log('Connected successfully');
  socket.end();
});

socket.on('error', (err) => {
  console.error('TLS error:', err.message);
});

For https.request():

javascript
const https = require('https');

https.request({
  hostname: '192.168.1.100',
  port: 443,
  path: '/api/endpoint',
  servername: 'api.example.com', // This matches the certificate
  method: 'GET'
}, (res) => {
  console.log('Success');
}).end();
2Use an HTTP Agent with proper TLS options

When making requests through an agent, configure the servername properly:

javascript
const https = require('https');

const agent = new https.Agent({
  hostname: '192.168.1.100',
  servername: 'api.example.com', // Ensure this matches certificate
  rejectUnauthorized: true,
  ca: [certificateBuffer]
});

https.request({
  hostname: '192.168.1.100',
  agent: agent,
  servername: 'api.example.com'
}, (res) => {
  console.log('Status:', res.statusCode);
}).end();

If using axios or other HTTP libraries:

javascript
const axios = require('axios');
const https = require('https');

const agent = new https.Agent({
  servername: 'api.example.com',
  rejectUnauthorized: true,
  ca: [certificateBuffer]
});

axios.get('https://192.168.1.100/api/endpoint', {
  httpAgent: agent,
  httpsAgent: agent
});
3Verify the certificate's Subject Alternative Names (SANs)

Check what hostnames the certificate is valid for:

bash
# View certificate details
openssl x509 -in certificate.pem -text -noout

# Look for the "Subject Alternative Name" section
# Example output:
# Subject Alternative Name:
#     DNS:api.example.com
#     DNS:api.alt.com
#     IP Address:192.168.1.100

If the hostname you're connecting to is not in the SAN list or CN, the connection will fail. You have two options:

1. Connect using one of the hostnames that IS on the certificate
2. Get a new certificate that includes your desired hostname

To check from Node.js:

javascript
const tls = require('tls');
const fs = require('fs');

const cert = fs.readFileSync('certificate.pem', 'utf8');
const decoded = require('crypto').createSecureContext(cert);

// The cert object will contain SAN information
console.log('Certificate details loaded');
4When connecting to IP addresses, use the correct hostname

If your server is behind an IP address but has a domain certificate, connect to the domain instead:

Bad (will fail SNI validation):

javascript
const https = require('https');

https.request({
  hostname: '192.168.1.100',  // IP address
  port: 443,
  path: '/api/data',
  servername: '192.168.1.100' // Can't match certificate CN/SAN
}, callback).end();

Good (proper SNI):

javascript
const https = require('https');

https.request({
  hostname: 'internal.company.com', // Domain with certificate
  port: 443,
  path: '/api/data',
  servername: 'internal.company.com'
}, callback).end();

If you must use an IP address, ensure:
1. The certificate includes the IP address in its SAN
2. Or disable certificate verification only in development (NOT production):

javascript
// DEVELOPMENT ONLY - Never do this in production
const agent = new https.Agent({
  rejectUnauthorized: false
});
5Handle Host header proxying correctly

When acting as a proxy, don't blindly use the incoming Host header for SNI:

Bad (SNI mismatch when proxying):

javascript
const https = require('https');

// Client sends: Host: client.example.com
// But we're forwarding to upstream.internal.com
const hostHeader = req.headers.host; // "client.example.com"

https.request({
  hostname: 'upstream.internal.com',
  servername: hostHeader, // WRONG! Should be upstream.internal.com
  path: req.url
}, callback).end();

Good (explicit SNI for upstream):

javascript
const https = require('https');

https.request({
  hostname: 'upstream.internal.com',
  servername: 'upstream.internal.com', // Matches upstream certificate
  path: req.url,
  headers: {
    'Host': req.headers.host // Client's original host
  }
}, callback).end();

The SNI must match the certificate, while the Host header can be anything you're forwarding.

6Use checkServerIdentity for custom validation

For advanced scenarios, override certificate validation with custom logic:

javascript
const tls = require('tls');
const https = require('https');

// Custom validation function
function checkServerIdentity(servername, cert) {
  // Allow specific mismatches in development
  if (process.env.NODE_ENV === 'development') {
    const allowedHosts = ['localhost', '127.0.0.1', 'internal.local'];
    if (allowedHosts.includes(servername)) {
      return undefined; // Validation passes
    }
  }

  // Otherwise use default validation
  return tls.checkServerIdentity(servername, cert);
}

const agent = new https.Agent({
  checkServerIdentity: checkServerIdentity,
  servername: 'api.example.com'
});

https.request({
  hostname: '192.168.1.100',
  agent: agent,
  servername: 'api.example.com'
}, callback).end();

Important: Only override validation in development environments with trusted certificates.

7Test SNI configuration with openssl

Verify your SNI setup before testing in Node.js:

bash
# Connect to server with explicit SNI name
openssl s_client -connect 192.168.1.100:443 -servername api.example.com

# You should see:
# Verify return code: 0 (ok)
# subject=CN = api.example.com

# If you get a certificate mismatch, the certificate doesn't cover that hostname

If openssl succeeds but Node.js fails, check your Node.js code for:
1. Missing servername option
2. Incorrect servername value
3. Old Node.js version without proper TLS support

Test directly in Node.js:

javascript
const tls = require('tls');

tls.connect({
  host: '192.168.1.100',
  port: 443,
  servername: 'api.example.com',
  rejectUnauthorized: true
}, () => {
  console.log('Connected successfully');
  process.exit(0);
}).on('error', (err) => {
  console.error('TLS Error:', err.message);
  process.exit(1);
});

Advanced notes

Understanding SNI in Modern TLS

SNI (Server Name Indication) is a critical feature for hosting multiple HTTPS sites on a single IP address. Before SNI, each HTTPS site needed its own IP. SNI allows servers to determine which certificate to send based on the hostname sent by the client during the TLS handshake.

SNI is sent unencrypted as part of the TLS Client Hello, so the server knows which certificate to send before the encrypted connection is established. Node.js should automatically handle SNI, but you must ensure you're specifying the correct servername.

Difference Between Host Header and SNI

- SNI: Sent during TLS handshake, determines which certificate the server sends
- Host Header: Sent in HTTP headers after TLS connection is established, determines which virtual host on the server handles the request

These can be different when proxying:

bash
Client → Proxy → Upstream Server

TLS handshake uses servername: upstream.internal.com
HTTP Host header can be: client.example.com

Certificate Subject Alternative Names (SANs)

Modern certificates use SANs instead of just the Common Name (CN). A certificate can be valid for multiple hostnames:

bash
Certificate valid for:
- api.example.com (CN)
- api.example.com (SAN)
- api-alt.example.com (SAN)
- *.internal.company (SAN wildcard)

Your SNI must match one of these exactly (or match a wildcard).

Wildcard Certificates and SNI

Wildcard certificates like *.example.com will match:
- api.example.com ✓
- auth.example.com ✓
- example.com ✗ (wildcard doesn't match base domain)

SNI with wildcard:

javascript
tls.connect({
  servername: 'api.example.com', // Matches *.example.com
  // ...
});

Self-Signed Certificates in Development

For development with self-signed certificates, create one with the correct hostnames:

bash
# Generate self-signed cert with multiple names
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
  -days 365 -nodes \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,DNS:127.0.0.1,DNS:*.local"

# Use in Node.js
const tls = require('tls');
const fs = require('fs');

tls.createServer({
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
}, (socket) => {
  socket.write('Hello from TLS server\n');
  socket.end();
}).listen(443);

HTTPS/TLS Error Categories

Node.js TLS errors include:
- ERR_TLS_CERT_ALTNAME_INVALID: Hostname doesn't match certificate
- ERR_TLS_SNI_NAME_MISMATCH: SNI name doesn't match certificate
- ERR_TLS_CERT_EXPIRED: Certificate expired
- ENOTFOUND: Hostname can't be resolved to IP
- ECONNREFUSED: Connection refused (target not listening)

This error specifically indicates the certificate is valid but the SNI doesn't match its CN/SANs.

Production Considerations

In production:
- Always set rejectUnauthorized: true (the default)
- Use certificates from trusted CAs
- Ensure SNI matches your certificate exactly
- Monitor certificate expiration dates
- Use environment variables for different configurations per environment

Example production setup:

javascript
const https = require('https');
const fs = require('fs');

const agent = new https.Agent({
  servername: process.env.TLS_SERVERNAME,
  rejectUnauthorized: true,
  ca: fs.readFileSync(process.env.CA_BUNDLE)
});

https.request({
  hostname: process.env.API_HOST,
  agent: agent,
  servername: process.env.TLS_SERVERNAME
}, callback).end();

Set environment variables per deployment:

bash
API_HOST=api.example.com
TLS_SERVERNAME=api.example.com
CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt

Frequently asked questions

What does "ERR_TLS_SNI_NAME_MISMATCH" mean?
The TLS SNI (Server Name Indication) mismatch error happens when the server name sent to the TLS server during the handshake doesn't match the certificate's Subject Alternative Names (SANs) or Common Name (CN). SNI is an extension to the TLS protocol that allows clients to specify which hostname they're connecting to before the server presents its certificate. This is critical for servers hosting multiple TLS certificates (one per domain).
How do I fix "ERR_TLS_SNI_NAME_MISMATCH"?
This error occurs when the hostname sent during TLS handshake (Server Name Indication) does not match the hostname on the SSL/TLS certificate. The server receives a different SNI name than what the certificate is valid for, causing verification to fail.
What causes "ERR_TLS_SNI_NAME_MISMATCH"?
Connecting to an IP address when certificate is issued for a specific hostname Using a Host header that differs from the actual target hostname without explicitly setting SNI Proxying HTTPS requests with request Host headers that don't match the upstream certificate
Is the "ERR_TLS_SNI_NAME_MISMATCH" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading