Mailerflow Start free
Blog › Transactional Email & Developer Tools › SMTP Email Sending

SMTP Connection Timeouts and How to Fix Them

Published November 11, 2026 · Updated November 11, 2026 · 8 min read

Illustration for SMTP Connection Timeouts and How to Fix Them

An SMTP connection timeout means the TCP handshake to your mail server never completed: the client waited for a socket that was never established, so no banner, no EHLO and no authentication error ever appeared. That single fact narrows the problem down considerably, because it rules out credentials, message content and most deliverability issues. What is left is the network path, the port, the DNS record or the TLS negotiation — and each of those has a distinct signature you can test for in a few minutes.

What an SMTP connection timeout actually tells you

SMTP failures look similar in a log but happen at four different stages, and the fix depends entirely on which stage broke.

  • Connection timeout — the TCP handshake never finished. Nothing was sent. The server may not even know your client exists.
  • Connection refused — something answered immediately and said no. The port is closed or no service is listening. This is a fast failure, not a timeout.
  • Authentication failed — TCP and TLS both succeeded, so the network is fine and the problem is credentials or permissions.
  • Read timeout after the banner — the socket opened, the server sent 220 ..., then stalled. This is a server-side or middleware stall, not a connect problem.

A typical Python smtplib failure looks like this (illustrative example, not a real log):

Traceback (most recent call last):
  File "send.py", line 12, in <module>
    with smtplib.SMTP("smtp.example.com", 587, timeout=30) as s:
  File "/usr/lib/python3.11/smtplib.py", line 255, in __init__
    (code, msg) = self.connect(host, port)
  File "/usr/lib/python3.11/smtplib.py", line 341, in connect
    self.sock = self._get_socket(host, port, self.timeout)
TimeoutError: [Errno 110] Connection timed out

Node with Nodemailer produces the same shape: Error: Connection timeout with code: 'ETIMEDOUT' and command: 'CONN'. When people search for "smtp timed out" or paste that error into a ticket, they are usually looking at one of these four stages. Identify the stage first; everything after that is mechanical.

For a wider view of the market, our ranking of the Best Email Marketing Platforms shows what each service really costs for your number of contacts.

Symptoms: how to tell a timeout from a rejection

Map what you observe to the stage, because the symptom is often the fastest diagnostic you have:

  • The process hangs for 30–120 seconds and then throws ETIMEDOUT or "Connection timed out". Classic connect-stage failure.
  • It fails only from CI, a specific VPC or a container, and works from your laptop. This is almost always egress rules or DNS, not code.
  • It times out on port 465 but succeeds on 587 (or the reverse). Port and encryption mode are mismatched.
  • It started right after a DNS change, a firewall rollout or a proxy migration. Something in the path changed.
  • It is intermittent, with some connections succeeding. Think rate limiting, IP reputation, NAT state or IPv6 fallback.
  • It fails instantly with "Connection refused" rather than hanging. That is not a timeout — the port is closed.

If the failure is environment-specific, treat the code as innocent until proven otherwise. The same client library, same credentials and same hostname behaving differently on two networks points at the network.

Most likely causes, in order of probability

1. Outbound port 25 is blocked. Most cloud providers and many ISPs block outbound port 25 by default to limit spam. This is the single most common cause on fresh VMs.

2. Firewall, security group or corporate proxy. Egress rules that allow 443 but not 587 or 465 will produce exactly this timeout. Proxies that do not support CONNECT to SMTP ports behave the same way.

3. Wrong port and encryption pairing. 587 expects STARTTLS, 465 expects implicit TLS, 25 is plain or opportunistic. Pairing 465 with STARTTLS makes the client wait for a handshake the server never starts.

4. DNS resolution failure or a stale record. If the SMTP hostname does not resolve, or resolves to an old IP, the client waits on a destination that will never answer. Split-horizon DNS inside a VPC is a frequent culprit.

5. TLS handshake mismatch. The client waits on a handshake the server never initiates, or negotiates a protocol version the server rejects silently. This looks like a timeout even though TCP succeeded.

6. IPv6 attempted with no route. If the hostname has an AAAA record but your network has no IPv6 route, the client may wait for the AAAA attempt to time out before falling back — or not fall back at all.

7. Provider-side rate limiting or reputation blocking. Some providers drop packets silently instead of rejecting, which the client experiences as a timeout rather than an error.

8. MTU, NAT or stateful firewall issues. Long-lived connections through NAT can stall when state expires. Less common, but it explains timeouts that only appear on large or slow transfers.

How to diagnose each cause

Test raw reachability

nc -vz smtp.example.com 587
telnet smtp.example.com 587

A hang here means the network path is the problem. An immediate "Connection refused" means the port is closed. A 220 banner means TCP works and you should move to the TLS layer.

Test TLS separately

# STARTTLS on 587
openssl s_client -connect smtp.example.com:587 -starttls smtp

# Implicit TLS on 465
openssl s_client -connect smtp.example.com:465

If nc succeeds but openssl hangs after printing CONNECTED, you have a TLS or port-mode mismatch. If it prints a certificate chain, TLS is fine.

Check DNS and IPv6

dig +short smtp.example.com
dig +short AAAA smtp.example.com
dig +short MX example.com

Compare the resolved IP against what your provider documents. If an AAAA record exists, test the IPv6 hypothesis by forcing IPv4 in code — family: 4 in Nodemailer, or resolving to A records before connecting in Python.

See where packets stop

traceroute -T -p 587 smtp.example.com
mtr --tcp --port 587 smtp.example.com

A trace that dies inside your own network is a firewall or security group issue. One that dies at the provider edge is worth a support ticket.

Inspect the local firewall and egress rules

sudo ufw status
sudo iptables -L -n

On cloud instances, check the security group's egress rules, not just ingress. Egress is the direction people forget.

Compare environments

Run the same nc and openssl commands from your laptop and from the failing server. If the laptop works and the server does not, the code is not the problem — the network path is.

A quick protocol-level view with curl -v smtp://smtp.example.com:587 confirms the same thing in one command and shows you exactly where it stalls.

Fixes step by step

Blocked port 25. Switch to 587 with STARTTLS or 465 with implicit TLS. Most providers document both; use whichever your host allows.

Firewall or proxy. Open egress for the chosen port in the security group and the host firewall, then allowlist the SMTP hostname in the proxy. If the proxy cannot do CONNECT to SMTP ports, route around it or use an API instead.

Port and encryption pairing. Match the mode to the port. Here are the correct combinations (illustrative config, not from a real project):

// Nodemailer: 587 + STARTTLS
const transporter = nodemailer.createTransport({
  host: 'smtp.example.com',
  port: 587,
  secure: false,        // STARTTLS upgrades the plain connection
  requireTLS: true,
  connectionTimeout: 10000,
  socketTimeout: 30000,
  auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
});
# Python smtplib: 587 + STARTTLS
import smtplib

with smtplib.SMTP("smtp.example.com", 587, timeout=10) as s:
    s.ehlo()
    s.starttls()
    s.login(user, password)
    s.send_message(msg)

For port 465, use secure: true in Nodemailer and smtplib.SMTP_SSL in Python. Never mix the two.

DNS problems. Use the provider's documented SMTP hostname rather than a hardcoded IP, flush the resolver cache, and check whether split-horizon DNS inside your VPC returns a different answer than the public resolver.

TLS mismatches. Fix the mode/port pairing first. Then update the CA bundle on the host. Disabling TLS verification or forcing legacy protocol versions is a diagnostic step only — never leave it in production.

IPv6 with no route. Force IPv4 in the client, or fix the AAAA record if it is wrong. Forcing IPv4 is the faster unblock.

Provider-side blocking. Check the provider's status page, review your sending IP reputation, and open a ticket with exact timestamps. Silent drops are a provider-side problem to confirm, not something you can fix in the client.

If you are sending transactional mail and would rather not maintain an outbound SMTP path at all, Mailerflow's transactional email API sends on a high-priority lane with templates and variables. Its REST API uses scoped keys and idempotency keys, and ships with an OpenAPI spec, code samples and a JS SDK, so retrying a failed send is safe rather than risky.

How to prevent it next time

  • Set explicit timeouts. A 10-second connect timeout and a 30-second socket timeout surface failures fast instead of hanging for two minutes on OS defaults.
  • Retry with backoff and jitter. Transient network errors deserve a retry; make sure the operation is idempotent so a retry cannot double-send.
  • Monitor connection success rate separately from delivery rate. A network problem that shows up as "deliverability" will send you chasing the wrong fix for days.
  • Pin the hostname and port in config and run a lightweight health check against them on a schedule.
  • Test from production's network, not just locally. CI is a good place for a daily connectivity check.
  • Keep a fallback port documented so an incident has a known second option.
  • Alert on ETIMEDOUT specifically. It is a distinct signal from bounces and complaints and should page differently.

If you use Mailerflow, signed real-time webhooks with retries and delivery logs make it clear whether a message actually left the system, which shortens this kind of investigation considerably.

When to ask your provider for help

Collect this before opening a ticket, because without it support can only guess:

  • Exact timestamps with timezone for at least three failed attempts
  • Source IP, destination hostname and port
  • The full error string, including the code (ETIMEDOUT, Errno 110)
  • Output of nc -vz and openssl s_client against the same host and port
  • Whether it reproduces from a different network

If nc succeeds but the TLS handshake stalls, that is a legitimate provider-side or path issue and worth escalating. If the timeout only happens from your own infrastructure, expect the provider to point back at your egress rules — so check those first and include what you found.

FAQ

Why does SMTP time out on port 25 but work on 587? Because outbound port 25 is blocked by many cloud hosts and ISPs to reduce spam. Port 587 with STARTTLS is the standard submission port and is usually allowed. Switch ports rather than trying to get 25 unblocked.

Why does it work locally but time out in production? The two environments have different network paths. Production likely has stricter egress rules, a proxy, or an IPv6 route that does not exist. Compare nc and openssl output from both.

Is an SMTP connection timeout the same as a delivery failure? No. A connection timeout means nothing was transmitted, so there is no message to bounce. A delivery failure happens after the server accepted the message. They need different monitoring.

Can TLS cause a connection timeout? Yes. If the client waits for a handshake the server never starts — usually because the port and encryption mode are mismatched — it looks identical to a network timeout. Test with openssl s_client to separate the two.

How long should an SMTP connect timeout be? Short: around 10 seconds for connect and 30 seconds for the socket, with retries and backoff. Long timeouts hide problems and delay your error handling.

Does switching to an API avoid this? It avoids your outbound SMTP path entirely, which removes ports 25, 587 and 465 from the equation. Your network still needs outbound HTTPS to reach the API, but that is a path you almost certainly already have open.

Send your first transactional email with the Mailerflow REST API — scoped keys, idempotency and an OpenAPI spec. Start free.