Practical guide

Nodemailer “connect ECONNREFUSED 127.0.0.1:587”: your code is mailing localhost

Leave the SMTP host empty and Nodemailer quietly tries your own machine. On our test the error came back as an AggregateError with an empty message, so a plain console.log(err.message) printed nothing.

The short answer. ECONNREFUSED to 127.0.0.1 or ::1 means Nodemailer tried a mail server on the same machine and nothing was listening. Usually the host option is undefined because SMTP_HOST is not set, so Nodemailer falls back to localhost. Set the variable on the host and restart. ECONNREFUSED to another IP means the host is wrong or the port is closed there.

Formgong

Nodemailer ECONNREFUSED 127.0.0.1:587: missing SMTP host
Nodemailer “connect ECONNREFUSED 127.0.0.1:587”: your code is mailing localhost

What we saw

On 16.10.2026 we created a transport with Node.js 22.23 and nodemailer 10.1.1 and host: process.env.SMTP_HOST while the variable was unset. Nodemailer did not complain about the missing host. It connected to localhost, found nothing on port 587, and failed in 66 ms.

The error was an AggregateError with code: "ESOCKET" and an empty message. The two real errors sat inside err.errors: connect ECONNREFUSED ::1:587 and connect ECONNREFUSED 127.0.0.1:587, one per address localhost resolves to. Code that logs only err.message prints a blank line here, which makes the problem look stranger than it is. Log the whole error object.

Reading the address in the error

ECONNREFUSED in Nodemailer
Address in the errorLikely cause
127.0.0.1:587 or ::1:587The host is empty or localhost. An unset SMTP_HOST, a typo in the variable name, or a config copied from a tutorial that ran its own mail server.
127.0.0.1:1025 and similarA local catcher such as MailHog or Mailpit in your dev config, not running, or the dev config shipped to production.
Some other IPThe host resolved, but nothing accepts that port there. On our test the typo smtp.gmial.com resolved to a real IP and refused port 587.

Inside Docker, localhost is the container itself, not your computer, so a mail catcher running on the host is “refused” from inside the container. Use the service name from your compose file, or host.docker.internal on Docker Desktop.

Fail early when the host is missing

Check the variables at startup so a missing host stops the server with its name in the log, then let verify() connect once before real traffic arrives.

JavaScript (Node.js)
// Fail at startup with a clear message instead of failing on the first visitor's message.
for (const name of ["SMTP_HOST", "SMTP_PORT", "SMTP_USER", "SMTP_PASS"]) {
  if (!process.env[name]) throw new Error(`${name} is not set`);
}

const transporter = nodemailer.createTransport({
  host: process.env.SMTP_HOST,
  port: Number(process.env.SMTP_PORT),
  secure: Number(process.env.SMTP_PORT) === 465, // true only for 465
  auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
});

await transporter.verify(); // connects and logs in, sends nothing

Do you need an SMTP host for a contact form?

If the SMTP login exists only so a contact form can email you, you can drop it: a form that posts to Formgong at https://formgong.com/submit has no SMTP host, port or password in your code, and Formgong sends the notification itself. See the HTML guide.

Frequently asked questions

Why does Nodemailer connect to 127.0.0.1 when I set a host?

The value your code passed was empty, usually an unset environment variable, so Nodemailer used localhost. Print typeof process.env.SMTP_HOST on the server to confirm.

Why is the Nodemailer error message empty?

On our test the failure came back as an AggregateError with an empty message; the details were in err.errors. Log the whole error object, not only err.message.

What does ECONNREFUSED mean?

The machine at that address answered, but no program accepts connections on that port. For a remote host it can also mean the port is closed or blocked.

Sources and documentation

Official references for this guide: Nodemailer: SMTP transport (host defaults), Node.js: Common system errors (ECONNREFUSED), MDN: AggregateError. HTML contact form, where data is stored.

Rate this guide

Your rating

We read every rating. A comment appears here only after we approve it.

← Back to the blog