Writing
Security
Mingtindu Sherpa9 min read

Fix Nodemailer TLS Certificate Hostname Errors

What ERR_TLS_CERT_ALTNAME_INVALID means when Nodemailer connects to an SMTP server, why it happens, and how to fix it without disabling certificate verification.

On this page

Nodemailer can fail to send mail with an error that looks like a networking problem but is actually about identity verification: the TLS certificate the SMTP server presented does not cover the hostname Nodemailer connected to.

ERR_TLS_CERT_ALTNAME_INVALID:
Hostname/IP does not match certificate's altnames

This article explains what that error means, why it's different from most other SMTP connection failures, and how to fix it correctly.

What the error actually checks

Every TLS connection, SMTP included, verifies two separate things: that the certificate is signed by a trusted authority, and that the certificate was actually issued for the host you're connecting to. The second check compares the hostname you connected with against the certificate's Subject Alternative Names (SAN) — the list of hostnames the certificate is valid for.

Node.js performs this comparison in tls.checkServerIdentity(hostname, cert). Per the Node.js TLS documentation, it returns an Error populated with reason, host, and cert when the hostname doesn't match any entry in subjectaltname, and undefined on success. ERR_TLS_CERT_ALTNAME_INVALID is that failure surfacing as a thrown error.

This means the connection itself worked, and the certificate itself may be entirely valid — just not valid for the hostname you used.

Four different things that are easy to confuse

A working SMTP configuration depends on four values that are conceptually separate, even though they often look similar:

ValueWhat it is
SMTP hostnameThe host Nodemailer opens a TCP/TLS connection to (smtp.example.com).
Certificate SAN entriesThe hostname(s) the SMTP server's TLS certificate was actually issued for.
Mailbox addressThe user/from email address (billing@example.com) — unrelated to either of the above.
SMTP port and encryption mode465 (implicit TLS) vs. 587 (STARTTLS) vs. 25 — determines when TLS negotiation happens, not which hostname is checked.

ERR_TLS_CERT_ALTNAME_INVALID is specifically a mismatch between the first two. It has nothing to do with your mailbox address, and nothing to do with your SMTP username or password — this is not an authentication error, even though it happens during connection setup and can feel like one.

Why the mismatch happens

The most common cause is connecting to a hostname that isn't the one the certificate was issued for — even when the underlying server is correct. This happens when:

  • The SMTP_HOST value is an IP address instead of a hostname. Certificates are issued for hostnames; connecting by IP means there is normally nothing in the SAN list to match against, unless the certificate specifically lists that IP.
  • The SMTP_HOST value is an internal, load-balancer, or CDN-fronted hostname that differs from the public hostname the certificate actually covers.
  • The provider migrated or renamed its mail infrastructure and the old hostname you're still using now points to (or is proxied through) a server presenting a different certificate.
  • A self-signed or internally issued certificate simply doesn't have your hostname in its SAN list at all.

Fixing it: use the certificate-covered hostname

The direct fix is to connect using the exact hostname your SMTP provider documents and that the certificate actually covers — not an IP address, and not an internal alias, unless the provider specifically states that alias is what the certificate is issued for:

const nodemailer = require("nodemailer");
 
const transporter = nodemailer.createTransport({
  host: process.env.SMTP_HOST, // must match the certificate's SAN entries
  port: Number(process.env.SMTP_PORT) || 587,
  secure: false, // false = STARTTLS on 587; true = implicit TLS on 465
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASSWORD,
  },
});

Check your provider's SMTP documentation for the exact hostname it issues certificates for — this is provider-specific, and guessing at a plausible-looking hostname is how the mismatch happens in the first place.

If you must connect by IP address

If a network constraint genuinely requires connecting by IP (rare for outbound SMTP, but possible behind certain firewalls or split-horizon DNS setups), Nodemailer exposes tls.servername for exactly this case. Per Nodemailer's SMTP documentation, tls.servername sets the hostname used for certificate validation independently of the host you connect to — and is "required when host is set to an IP address":

const transporter = nodemailer.createTransport({
  host: "203.0.113.10", // example IP — replace with the provider's documented address
  port: 587,
  secure: false,
  tls: {
    servername: "smtp.example.com", // the hostname the certificate is actually issued for
  },
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASSWORD,
  },
});

This keeps full certificate validation active — it just tells Node.js which hostname to validate the certificate against, separately from where the TCP connection goes.

Understanding secure vs. STARTTLS

Nodemailer's secure option is not "TLS on or off" — both settings encrypt the connection in a correctly configured setup, they just differ in when:

  • secure: true — implicit TLS. The TLS handshake happens immediately when the connection opens. Typically port 465.
  • secure: false — the connection starts in plaintext, then upgrades to TLS via STARTTLS "automatically if it's supported by the server, unless you explicitly disable it" per the Nodemailer SMTP documentation. Typically port 587 (or 25).

secure: false does not mean unencrypted. It means STARTTLS negotiation, which is the standard mode for port 587. Setting requireTLS: true makes the STARTTLS upgrade mandatory — the connection fails outright rather than silently falling back to plaintext if the server doesn't offer it. ignoreTLS: true disables STARTTLS entirely and should not be used for a production mail path.

The certificate hostname check happens as part of this same TLS negotiation, regardless of which mode you're in — implicit TLS and STARTTLS both end up validating the certificate against the hostname you connected to.

What not to do: disabling certificate verification

Nodemailer's tls object accepts rejectUnauthorized: false, which disables certificate verification — including the hostname check — entirely.

// Do not use this as a production fix.
tls: {
  rejectUnauthorized: false,
}

This makes the error disappear because Node.js stops checking the certificate at all, not because the underlying mismatch was resolved. The Node.js TLS documentation is explicit that rejectUnauthorized defaults to true for this reason: with it disabled, there is no verification that you're actually talking to your real SMTP provider rather than an attacker positioned between you and it (a man-in-the-middle). Credentials sent over that connection — your SMTP username and password — are exposed to whoever is in that position.

If you need the connection to succeed while you sort out the correct hostname, prefer a temporary, clearly-labeled development-only override, and never ship rejectUnauthorized: false to a production mail path.

A short diagnostic sequence

  1. Read the exact hostname from SMTP_HOST (without printing the full transport config, which includes credentials).
  2. Confirm that hostname against your provider's current SMTP documentation — not an old configuration example or a previous provider's docs.
  3. Confirm whether SMTP_HOST is an IP address. If so, that's almost certainly the mismatch; switch to the documented hostname or set tls.servername.
  4. Confirm the port and secure value match what the provider documents for that hostname (implicit TLS vs. STARTTLS use different ports).
  5. Retry with the corrected hostname and capture the sanitized result.

Common mistakes

Treating this as an authentication failure

ERR_TLS_CERT_ALTNAME_INVALID happens during the TLS handshake, before SMTP authentication is even attempted. Changing the SMTP username or password will not fix it.

Copying a hostname from an old integration or a different provider's example

SMTP hostnames are provider-specific and sometimes account-specific. A hostname that worked for a previous mail provider, or that appears in a generic tutorial, is not guaranteed to match your current provider's certificate.

Reaching for rejectUnauthorized: false as the first fix

It resolves the symptom, not the cause, and removes protection against the exact class of failure (server impersonation) this check exists to catch.

Publishing SMTP credentials while debugging

When sharing logs, screenshots, or a sanitized .env for troubleshooting, remove SMTP_USER and SMTP_PASSWORD entirely — don't just blur them. Never paste a complete .env file, sanitized or not, into a public issue, forum post, or article; recreate only the specific lines that are relevant with placeholder values.

Verification checklist

  • SMTP_HOST is set to the exact hostname your provider documents for its certificate, not an IP address or internal alias.
  • SMTP_PORT and secure match the connection mode your provider expects for that hostname.
  • The application connects and authenticates successfully with rejectUnauthorized left at its default (true).
  • No .env values, SMTP credentials, or full connection details appear in shared logs or screenshots.
  • Sending a real test message succeeds after the fix, and the original error no longer appears in logs.

Claims to manually verify before publishing

  • Claim: You personally encountered ERR_TLS_CERT_ALTNAME_INVALID in a specific project.

    • Why verification is needed: This draft was written from Node.js/Nodemailer documentation and the error pattern supplied for the article, not a captured incident.
    • Suggested evidence: The sanitized original error output, the SMTP host/port/secure values in use (with credentials removed), and confirmation of which change resolved it.
  • Claim: Switching to the provider-documented hostname (or setting tls.servername) was the fix that worked.

    • Why verification is needed: This is the technically correct fix per Node.js/Nodemailer documentation, but it has not been confirmed against a real failing case in this project.
    • Suggested evidence: Before-and-after sanitized connection logs showing the hostname change and a successful send.
  • Claim: The mail provider referenced in your final version documents the hostname/certificate behavior described here.

    • Why verification is needed: Certificate SAN coverage and documented hostnames are provider-specific and can change.
    • Suggested evidence: A link to the specific provider's current SMTP setup documentation.

Related writing

Share