---
title: "Simple Mail Transfer Protocol (SMTP)"
description: "SMTP is the protocol mail servers use to transfer email. Understanding its conversation is what makes mail log lines and rejection codes readable."
url: "https://mailmum.io/glossary/simple-mail-transfer-protocol-smtp/"
---

# Simple Mail Transfer Protocol (SMTP)

The Simple Mail Transfer Protocol (SMTP) is the protocol mail servers use to
hand messages to one another, and that mail clients use to submit new messages
for sending. It is defined in
[RFC 5321](https://www.rfc-editor.org/rfc/rfc5321).

SMTP is a line-based conversation in plain text. That is worth knowing not out
of nostalgia but because every rejection you will ever debug is a line from this
conversation, quoted back at you in a log file.

## What does an SMTP session look like?

Client lines are the commands; the numbered lines are the server's replies:

```txt
$ telnet mx1.example.net 25
220 mx1.example.net ESMTP Postfix
EHLO mail.example.com
250-mx1.example.net
250-PIPELINING
250-SIZE 20480000
250-STARTTLS
250 8BITMIME
MAIL FROM:<sender@example.com>
250 2.1.0 Ok
RCPT TO:<recipient@example.net>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
From: Sender <sender@example.com>
To: Recipient <recipient@example.net>
Subject: Test

This is the message body.
.
250 2.0.0 Ok: queued as 4Rk8Zt1234
QUIT
221 2.0.0 Bye
```

Every acceptance decision a receiving server makes happens at one of those
steps, which is why "where in the conversation was it rejected" is the first
question when mail goes missing.

## What is the envelope, and why does it differ from the headers?

`MAIL FROM` and `RCPT TO` are the *envelope*. They are what the servers act on:
where a [bounce](https://mailmum.io/glossary/bounce-message/) goes and where the message is
actually delivered.

`From:`, `To:` and `Cc:` are *headers*. They are part of the message content and
are what the recipient sees. Nothing in SMTP requires the two to agree.

This gap is not a flaw so much as a design: it is what makes mailing lists and
`Bcc` work. It is also the root of a great deal of
[abuse](https://mailmum.io/glossary/abuse/), and the reason
[SPF](https://mailmum.io/glossary/sender-policy-framework-spf/) authenticates the envelope while
[DMARC](https://mailmum.io/glossary/domain-based-message-authentication-reporting-and-conformance-dmarc/)
exists to tie that result back to the visible `From:`.

## What do the response codes mean?

| Class | Meaning | Sender's reaction |
| --- | --- | --- |
| `2xx` | success | continue |
| `3xx` | intermediate, send more | continue |
| `4xx` | temporary failure | retry later, keep queued |
| `5xx` | permanent failure | give up, generate a bounce |

The distinction between `4xx` and `5xx` decides everything downstream. A `450`
keeps the message in the [queue](https://mailmum.io/glossary/email-queue/) for retry; a `550`
produces a non-delivery report at once.

This is also the mechanism behind greylisting: a server answers `450` to an
unknown sender on first contact, expecting a legitimate server to retry a few
minutes later while a great deal of bulk-sending software never will.

## Which ports are used, and for what?

* **Port 25** &mdash; server-to-server transfer. Accepts mail for local
  recipients from anyone, without authentication. Many consumer ISPs block
  outbound connections to it.
* **Port 587** &mdash; the submission service, defined in
  [RFC 6409](https://www.rfc-editor.org/rfc/rfc6409). Authentication is
  mandatory, and TLS is negotiated with `STARTTLS`.
* **Port 465** &mdash; submission with TLS established before the greeting
  (implicit TLS). Deprecated for years, then re-standardised, and now widely
  used again.

Treating 25 and 587 as interchangeable is how a server becomes an open relay.
See [access management](https://mailmum.io/glossary/access-management-in-email-systems/).

## Where do the checks hook into the conversation?

Each stage is an opportunity to reject, and rejecting early is cheaper and
safer:

| Stage | What is checked |
| --- | --- |
| connection | client IP against local maps and [DNSBLs](https://mailmum.io/glossary/dns-blocklist-dnsbl/) |
| `EHLO` | the [hostname](https://mailmum.io/glossary/hostname/), and its [reverse DNS](https://mailmum.io/glossary/reverse-dns-rdns/) |
| `MAIL FROM` | envelope sender, SPF |
| `RCPT TO` | does the recipient exist, is relaying permitted |
| `DATA` / end of data | content scanning, DKIM verification, DMARC |

Rejecting an unknown recipient at `RCPT TO` rather than accepting and bouncing
afterwards is what prevents [backscatter](https://mailmum.io/glossary/backscatter/) &mdash; the
single most consequential configuration choice in this table.

## How do you test an SMTP server by hand?

`telnet` or `openssl s_client` to the port and type the commands above. For
TLS-only submission:

```sh
$ openssl s_client -connect mx1.example.net:465 -quiet
220 mx1.example.net ESMTP Postfix
EHLO mail.example.com
```

For scripted testing, sending through the local
[MTA](https://mailmum.io/glossary/mail-transfer-agent-mta/) is usually more convenient than
speaking the protocol directly &mdash; see
[sending test emails from shell](https://mailmum.io/posts/sending-test-emails-from-shell/).
