Skip to main content

Proxy support

Nodemailer can route SMTP connections through an outbound proxy server. This is useful when your application runs behind a corporate firewall or when you need to route traffic through a specific network path. Proxy support works with both single connections and pooled connections.

Nodemailer includes built-in support for HTTP CONNECT proxies. For SOCKS4/4a/5 proxies or other protocols, you have two options:

  1. Install the socks package and register it with the transporter via transporter.set("proxy_socks_module", require("socks")).
  2. Write a custom proxy handler function for specialized requirements.

Quick start

To use a proxy, set the proxy option to a URL string when creating your transporter. Nodemailer parses the URL and automatically determines how to establish the tunnel.

const nodemailer = require("nodemailer");

const transporter = nodemailer.createTransport({
host: "smtp.example.com",
port: 465,
secure: true,
proxy: "http://proxy.example.test:3128", // HTTP proxy URL
});

HTTP CONNECT proxies

HTTP CONNECT proxies work out of the box with no additional dependencies. Nodemailer supports both http:// and https:// proxy URLs. If your proxy requires authentication, include the credentials in the URL (for example, http://user:pass@proxy.example.com:3128).

const transporter = nodemailer.createTransport({
host: "smtp.example.com",
port: 465,
secure: true,
proxy: process.env.HTTP_PROXY, // You can also use HTTPS_PROXY
});

SOCKS proxies

SOCKS proxy support is not bundled with Nodemailer to keep the package lightweight. To use SOCKS proxies, you need to install the socks package separately and register it with the transporter.

First, install the package:

npm install socks --save

Then configure the transporter and register the SOCKS module:

const transporter = nodemailer.createTransport({
host: "smtp.example.com",
port: 465,
secure: true,
proxy: "socks5://127.0.0.1:1080",
});

// Register the socks module so Nodemailer can use it
transporter.set("proxy_socks_module", require("socks"));

Supported URL protocols

Use one of the following URL schemes depending on your proxy type:

ProtocolProxy typeDescription
socks4:SOCKS4Basic SOCKS4 protocol
socks4a:SOCKS4aSOCKS4 with remote DNS resolution
socks5:SOCKS5SOCKS5 with authentication and IPv6 support
socks:SOCKS5Alias for socks5:

Local testing with SSH

You can create a quick SOCKS5 proxy for testing by using SSH dynamic port forwarding. This tunnels all traffic through an SSH connection to a remote server.

Run this command to start the proxy:

ssh -N -D 0.0.0.0:1080 user@remote.host

The -N flag tells SSH not to execute a remote command (just forward ports), and -D 0.0.0.0:1080 creates a SOCKS proxy listening on port 1080.

Then configure Nodemailer to use this proxy:

proxy: "socks5://127.0.0.1:1080"

Prefer an IP literal for the proxy address. Non-IP proxy hostnames are resolved with dns.resolve(), which queries DNS servers directly and does not consult /etc/hosts, so names like localhost may fail to resolve.

Custom proxy handlers

If you need special authentication, a proprietary protocol, or any other custom proxy logic, you can write your own handler function. This gives you full control over how the socket connection is established.

To register a custom handler, use transporter.set() with a key in the format proxy_handler_<protocol>, where <protocol> matches the URL scheme you use in the proxy option.

const transporter = nodemailer.createTransport({
host: "smtp.example.com",
port: 465,
secure: true,
proxy: "myproxy://127.0.0.1:9999",
});

// Register a handler for the "myproxy:" URL scheme
transporter.set("proxy_handler_myproxy", (proxy, options, done) => {
const net = require("net");

console.log("Connecting to proxy at %s:%s", proxy.hostname, proxy.port);

const socket = net.connect(proxy.port, proxy.hostname, () => {
// Perform any custom handshake with your proxy here...

// Return the established socket to Nodemailer
done(null, { connection: socket });
});
});

The handler function receives three arguments:

  • proxy - A parsed URL object containing the proxy address details
  • options - Connection options including the target host and port
  • done - A callback to invoke with (error, result) when the connection is ready

Pre-encrypted connections

If your proxy connection is already encrypted (for example, you used tls.connect() instead of net.connect()), you must set secured: true in the result object. This tells Nodemailer that the socket is already TLS-encrypted, so it skips wrapping the socket in TLS again (the implicit upgrade normally performed when secure: true is set). Use this together with secure: true in the transport options.

const tls = require("tls");

transporter.set("proxy_handler_myproxys", (proxy, options, done) => {
const socket = tls.connect(proxy.port, proxy.hostname, () => {
// The secured flag indicates the connection is already TLS-encrypted
done(null, { connection: socket, secured: true });
});
});