The most surprising thing about SMTP is that it’s fundamentally a push protocol, designed for servers to send mail to each other, not for clients to send mail out.

Let’s watch it in action. We’ll use telnet to connect to an SMTP server (port 25) and have a chat. Most systems will have a mail server running locally, so localhost is a good bet. If not, you can try a public mail server like smtp.mail.yahoo.com (though they might block direct telnet connections for spam reasons).

telnet localhost 25

You should see something like this:

220 localhost ESMTP Postfix

The 220 is an SMTP reply code, meaning "Service ready." The text after it is a banner from the server, telling you its name and capabilities.

Now, we need to introduce ourselves. This is where EHLO (Extended Hello) comes in. It’s the modern way to say hello, asking the server what it can do.

EHLO mydomain.com

The server responds with a list of extensions it supports:

250-localhost
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN

Each line starting with 250 is an affirmative reply. The hyphenated lines are continuations. SIZE tells us the maximum email size it will accept. 8BITMIME means it can handle non-ASCII characters. DSN refers to Delivery Status Notifications (read receipts).

Next, we declare who the email is from. This is MAIL FROM:.

MAIL FROM:<sender@mydomain.com>

The server checks if this address is valid for initiating a transaction (not necessarily if the mailbox exists on its system, that’s for later).

250 2.1.0 Ok

Then, we tell it who the email is to. This is RCPT TO:. You can have multiple RCPT TO: commands for sending to multiple recipients.

RCPT TO:<recipient@otherdomain.com>

The server will again check if it can deliver to this recipient.

250 2.1.5 Ok

If we had more recipients, we’d repeat RCPT TO: for each. Once all recipients are specified, we signal that we’re ready to send the actual email content using the DATA command.

DATA

The server replies:

354 Start mail input; end with <CRLF>.<CRLF>

This 354 code means "Ok, send the data." It also tells you how to end the data block: a single period (.) on a line by itself, preceded and followed by a carriage return and line feed (<CRLF>).

Now, you type the email headers and body. Headers are key-value pairs, like Subject: or To:, and must be separated from the body by a blank line.

Subject: Test Email
To: recipient@otherdomain.com
From: sender@mydomain.com

This is the body of the email.

Finally, you end the data block:

.

The server will then process the email and tell you if it accepted it for delivery.

250 2.0.0 Ok: queued as ABCDEF12345

The 250 2.0.0 Ok means the server has accepted the mail and queued it. The queued as ABCDEF12345 is a message ID for tracking.

If you wanted to send another email, you’d issue a RSET command to clear the current transaction and start over with MAIL FROM:. To end the session, you use QUIT.

QUIT
221 2.0.0 Bye

The fundamental problem SMTP solves is enabling asynchronous, store-and-forward messaging between mail servers. It’s a simple, text-based protocol, which makes it easy to debug and understand, but also relatively inefficient and prone to issues like open relays if not secured properly. The EHLO command and its extensions are crucial for modern email, allowing servers to negotiate capabilities like larger message sizes, authentication methods, and encryption.

Most people don’t realize that the MAIL FROM: address specified in the SMTP transaction is the envelope sender, not necessarily the From: header you see in your email client. This envelope sender is used for bounce messages and is often different from the visible From: header, which can be forged.

The next concept you’ll likely run into is SMTP authentication (AUTH) to allow clients to send mail through a server, and TLS/SSL encryption (STARTTLS) to secure the communication.

Want structured learning?

Take the full Smtp course →