Nodemailer
The Nodemailer ssl3_get_record wrong version number error is an OpenSSL port/TLS mismatch. Use secure:false on port 587 (STARTTLS) and secure:true on 465.
Updated Jul 1, 2026
The short answer
This isn't a Nodemailer bug — it's an OpenSSL handshake error meaning your client sent a TLS handshake to a port that expected plaintext first. It almost always comes from setting secure: true on port 587. Fix it by using secure: false for port 587 (STARTTLS) and secure: true only for port 465 (implicit TLS).
This error is raised by OpenSSL (Node's TLS layer), not by Nodemailer itself. The full message looks like error:1408F10B:SSL routines:ssl3_get_record:wrong version number on OpenSSL 1.x, or error:0A00010B:SSL routines::wrong version number on OpenSSL 3.x (current in Node 18+, including Node 22 LTS) — the numeric code and function name differ between OpenSSL versions, but the underlying cause is identical. It means your code opened a TLS handshake immediately, but the server on that port replied with a plaintext SMTP greeting (e.g. 220 smtp.example.com ESMTP). OpenSSL tries to read the TLS record version field out of that plaintext, gets a nonsense value, and aborts.
In Nodemailer terms, this is almost always a mismatch between the secure flag and the port:
secure: true.STARTTLS command, so it needs secure: false.Setting secure: true on port 587 makes Nodemailer try to negotiate TLS against a server that is still speaking plaintext — producing the wrong-version-number error. (Both ports are valid and encrypted per RFC 8314; 465 is not deprecated.)
Note:
secure: falsedoes not send mail in the clear. As the Nodemailer SMTP docs state, the connection is still upgraded to TLS via STARTTLS once connected.
1. Match secure to your port. This resolves the vast majority of cases.
const nodemailer = require("nodemailer");// Port 587 — STARTTLSconst transporter = nodemailer.createTransport({host: "smtp.example.com",port: 587,secure: false, // upgrade later with STARTTLSauth: { user: "username", pass: "password" },});// Port 465 — implicit TLSconst transporterImplicit = nodemailer.createTransport({host: "smtp.example.com",port: 465,secure: true, // TLS from the first byteauth: { user: "username", pass: "password" },});
Nodemailer's default for secure is false, and it auto-sets true when port is 465. If you're hitting this error you've likely overridden that.
2. Force the STARTTLS upgrade if your provider requires encryption. With secure: false, add requireTLS: true so the send fails loudly rather than falling back to plaintext:
const transporter = nodemailer.createTransport({host: "smtp.example.com",port: 587,secure: false,requireTLS: true,auth: { user: "username", pass: "password" },});
3. Rule out the inverse mismatch. If you set secure: false on port 465, you'll see a hang or a different TLS error because the server never sends a plaintext greeting. Switch to secure: true.
4. Check for a proxy / wrong host. The same OpenSSL error appears if host actually points at a plain HTTP service, a load balancer terminating TLS elsewhere, or a corporate proxy. Confirm the endpoint really is an SMTP server on the port you specified, e.g. openssl s_client -starttls smtp -connect smtp.example.com:587.
For Outlook/Hotmail, smtp-mail.outlook.com listens on port 587 with STARTTLS, so secure: false is correct there too. Microsoft has also been retiring Basic Auth for Exchange Online/Office 365 — if auth fails after fixing the port, you may need OAuth2 rather than a username/password.
secure mismatch.nodemailer.createTransport("SMTP", {...}) signature was removed in Nodemailer v1. Use the single-object form shown above.References
FAQ
No. With secure: false on port 587, Nodemailer still upgrades the connection to TLS using STARTTLS once connected, as the official Nodemailer docs state. To guarantee the upgrade happens, add requireTLS: true so the send fails rather than falling back to plaintext.
One API, every provider
Courier connects to your email, SMS, and push providers, handles retries and failover, and surfaces delivery errors in plain language.
Reply-code definitions per RFC 8314 §3.3, §7.3. Last reviewed Jul 1, 2026. Courier is not affiliated with third-party providers; error behavior may vary by implementation.
© 2026 Courier. All rights reserved.