How SMTP Really Works
A complete walkthrough of an SMTP transaction — from DNS lookup to final delivery. If you send email programmatically, this is the mental model you need.
The Big Picture
SMTP — the Simple Mail Transfer Protocol — is the protocol that moves email across the internet. Defined in RFC 5321, it has been the backbone of email since 1982 (the original RFC 821). Despite decades of extensions, the core transaction is still a short, synchronous text conversation between two servers over TCP port 25.
Every email delivery follows the same sequence:
- DNS lookup — find the recipient's mail server
- TCP connection — connect to port 25 (or 465/587 for submission)
- Banner & EHLO — exchange identities and capabilities
- STARTTLS — upgrade to encrypted connection (usually)
- AUTH — authenticate if submitting (port 587)
- Envelope — MAIL FROM and RCPT TO
- DATA — transmit the message headers and body
- QUIT — close the connection
Step 1: DNS — Finding the Mail Server
Before any SMTP conversation begins, the sending server must discover where to deliver the message. It queries DNS for MX records on the recipient's domain.
10 mx1.example.com.
20 mx2.example.com.
The number is the priority (lower = preferred). The sender tries mx1.example.com first. If it is unreachable or returns a temporary error, the sender falls back to mx2.example.com.
If no MX records exist, the sender falls back to the domain's A or AAAA record — but this is a last resort. If an explicit Null MX record exists (0 .), the domain does not accept mail at all. See DNS and Mail Routing for the full algorithm.
Step 2: TCP Connection and the Banner
The sender opens a TCP connection to port 25 of the MX host. The receiving server speaks first with a 220 banner:
220 mx1.example.com ESMTP ready
The 220 status code means "service ready." If the server is overloaded, it may respond with 421 (service not available) and close the connection. The sender should retry later.
For message submission (a user's mail client sending through their provider), the connection goes to port 587 (RFC 6409) or port 465 (implicit TLS, RFC 8314) instead of port 25.
Step 3: EHLO — Announcing Capabilities
The sender introduces itself with EHLO (Extended HELO), providing its own hostname. The server responds with its supported extensions:
250-mx1.example.com Hello sender.mailertogo.com
250-SIZE 52428800
250-8BITMIME
250-STARTTLS
250-AUTH PLAIN LOGIN
250-PIPELINING
250-ENHANCEDSTATUSCODES
250 SMTPUTF8
Each 250- line advertises an extension. The last line uses 250 (space, not dash) to signal the end. Key extensions:
- SIZE — maximum message size in bytes
- STARTTLS — the server supports upgrading to TLS (RFC 3207)
- AUTH — supported authentication mechanisms (RFC 4954)
- PIPELINING — the sender can batch commands without waiting (RFC 2920)
- ENHANCEDSTATUSCODES — extended status codes like 5.1.1 (RFC 3463)
- 8BITMIME — 8-bit content transfer is supported
- SMTPUTF8 — internationalized email addresses (RFC 6531)
The legacy HELO command (without the "E") skips extensions entirely. It is almost never used in practice today.
Step 4: STARTTLS — Upgrading to Encryption
If the server advertised STARTTLS, the sender issues the command to upgrade the connection:
220 2.0.0 Ready to start TLS
# TLS handshake happens here
# Connection is now encrypted
# Sender must EHLO again over the encrypted channel
EHLO sender.mailertogo.com
250-mx1.example.com Hello sender.mailertogo.com
250-SIZE 52428800
250 ENHANCEDSTATUSCODES
After STARTTLS, the sender must send EHLO again. The server may advertise different extensions over the encrypted connection (for instance, AUTH is typically only advertised after TLS is established).
On port 465, TLS is implicit — the connection is encrypted from the first byte, and no STARTTLS step is needed.
Without TLS, the entire SMTP conversation — including passwords and message content — is sent in plaintext. Modern best practice is to always use TLS. See MTA-STS and DANE for mechanisms that enforce it.
Step 5: AUTH — Authentication (Submission Only)
When a mail client submits a message through port 587, it authenticates using SMTP AUTH (RFC 4954):
235 2.7.0 Authentication successful
The Base64 string encodes \0username\0password. This is why TLS is critical — without it, credentials travel in the clear.
Server-to-server delivery on port 25 does not use AUTH. Instead, the receiving server relies on the sender's IP address, SPF records, DKIM signatures, and other authentication mechanisms to verify the message. See Email Authentication Explained.
Step 6: The Envelope — MAIL FROM and RCPT TO
The envelope is separate from the message headers. It tells the receiving server who is sending the message and who should receive it:
250 2.1.0 OK
RCPT TO:<bob@example.net>
250 2.1.5 OK
RCPT TO:<carol@example.net>
250 2.1.5 OK
Key details:
-
MAIL FROM specifies the return path (also called the envelope sender or bounce address). This is where DSNs (bounce messages) go. It can differ from the
From:header. - RCPT TO specifies each recipient. You can have multiple RCPT TO commands for one message.
- The optional
SIZE=parameter lets the server reject oversized messages early. - A
MAIL FROM:<>(empty reverse path) is used for bounce messages themselves, to prevent infinite bounce loops.
Each command gets an individual response. A 250 means accepted. A 550 on RCPT TO means the mailbox does not exist. A 452 means the server is temporarily unable to accept that recipient. The sender must handle each response individually — some recipients may be accepted while others are rejected.
Step 7: DATA — Sending the Message
The DATA command begins message transmission:
354 Start mail input; end with <CRLF>.<CRLF>
From: Alice <alice@example.com>
To: Bob <bob@example.net>
Subject: Meeting tomorrow
Date: Tue, 11 Mar 2026 10:30:00 -0400
Message-ID: <abc123@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Hi Bob,
Are we still on for 2pm?
- Alice
.
250 2.0.0 OK: queued as 4F3A21C8
The message includes both headers and body, separated by a blank line. The message is terminated by a line containing only a single period (.) — the "dot-stuffing" convention. Any line in the message body that starts with a period has an extra period prepended during transmission and stripped on receipt.
The 250 response after DATA means the message has been accepted for delivery. It does not mean the message has been delivered to the recipient's mailbox. The receiving server may still reject it during content filtering, or generate a bounce later.
The queue ID (e.g., 4F3A21C8) is invaluable for debugging. Record it.
Step 8: QUIT
221 2.0.0 Bye
The sender closes the session. The TCP connection is torn down. If the sender has more messages for the same server, it can issue another MAIL FROM instead of QUIT to reuse the connection.
SMTP Response Codes
Every SMTP response starts with a three-digit code. The first digit tells you the category:
| Code | Meaning | Action |
|---|---|---|
| 2xx | Success | Command completed; proceed |
| 3xx | Intermediate | Server is waiting for more data (e.g., after DATA) |
| 4xx | Temporary failure | Retry later. The server may accept the message on a subsequent attempt |
| 5xx | Permanent failure | Do not retry. The message will never be accepted as-is |
Common codes you will encounter:
-
220— Service ready (banner) -
250— Requested action completed -
354— Start message input -
421— Service not available, closing connection (try again later) -
450— Mailbox temporarily unavailable (greylisting, rate limiting) -
451— Requested action aborted due to local error -
452— Insufficient storage -
550— Mailbox not found / action not taken -
551— User not local; please try a forwarding address -
552— Message size exceeds limit -
553— Mailbox name not allowed -
554— Transaction failed (often used for policy rejection)
With ENHANCEDSTATUSCODES, the server appends a more specific code like 5.1.1 (bad destination mailbox). See Understanding Email Bounces for a full breakdown.
A Complete Transaction
Here is an entire SMTP session from connection to close, annotated:
220 mx1.example.net ESMTP Postfix
# 2. Client introduces itself
EHLO sender.mailertogo.com
250-mx1.example.net
250-STARTTLS
250-SIZE 26214400
250-PIPELINING
250-ENHANCEDSTATUSCODES
250 8BITMIME
# 3. Upgrade to TLS
STARTTLS
220 2.0.0 Ready to start TLS
# (TLS handshake completes)
# 4. Re-introduce over encrypted channel
EHLO sender.mailertogo.com
250-mx1.example.net
250-SIZE 26214400
250-PIPELINING
250-ENHANCEDSTATUSCODES
250 8BITMIME
# 5. Envelope
MAIL FROM:<notifications@app.example.com>
250 2.1.0 OK
RCPT TO:<bob@example.net>
250 2.1.5 OK
# 6. Message content
DATA
354 End data with <CR><LF>.<CR><LF>
From: App Notifications <notifications@app.example.com>
To: bob@example.net
Subject: Your invoice is ready
Date: Tue, 11 Mar 2026 14:00:00 +0000
Message-ID: <inv-7842@app.example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Your March invoice is ready. Log in to view it.
.
250 2.0.0 OK: queued as B9C4E1A3F
# 7. Done
QUIT
221 2.0.0 Bye
Submission vs. Relay
SMTP serves two distinct roles:
- Submission (port 587 or 465): A mail client sends a new message to its mail server. Authentication is required. The server takes responsibility for delivering it.
- Relay (port 25): One mail server delivers a message to another. No authentication — authorization comes from DNS (MX records) and sender verification (SPF, DKIM).
An open relay — a server that accepts mail from anyone to anyone — is the cardinal sin of email configuration. Spammers exploit open relays immediately. Every modern mail server must restrict relaying to authenticated users or known senders.
What Can Go Wrong
Connection refused or timed out
The MX host is down or firewalled. The sender's queue retries with exponential backoff, cycling through lower-priority MX records. After the retry period (typically 4-5 days), the message bounces.
Greylisting
The receiving server returns 450 for the first attempt from an unknown sender. Legitimate servers retry; spammers typically do not. Your message will be delayed by minutes but will eventually deliver.
Rejected at RCPT TO
A 550 5.1.1 User unknown response means the mailbox does not exist. This is a hard bounce. Remove the address from your list immediately.
Rejected at DATA
The server may accept the envelope but reject the content. Common reasons: message too large, content flagged as spam, or failed authentication checks (SPF/DKIM/DMARC). The response comes after the terminating dot.
Accepted then bounced
The server returns 250 for DATA but later generates a DSN (bounce message) because the mailbox was over quota, content filtering rejected it, or an internal error occurred. This is the hardest case to handle — you only find out through the bounce message sent to the MAIL FROM address.
TLS failures
If STARTTLS negotiation fails, most servers fall back to plaintext. This is a security risk — an active attacker can strip TLS. MTA-STS and DANE prevent this downgrade.
Key Takeaways
- SMTP is a synchronous, text-based protocol. Every command gets a response code.
- The envelope (MAIL FROM / RCPT TO) is separate from the message headers (From: / To:). They can differ.
- A
250after DATA means "accepted for delivery," not "delivered to inbox." - 4xx errors are temporary — retry. 5xx errors are permanent — do not retry.
- Always use TLS. Enforce it with MTA-STS or DANE.
- Record queue IDs from server responses for debugging.