Что такое протокол SMTP и как идёт письмо от отправителя до ящика получателя
SMTP это текстовый протокол передачи почты поверх TCP: программа с письмом на руках открывает соединение с почтовым сервером и короткими командами сообщает, от кого письмо, кому его отдать и что лежит внутри. Дальше сервер отправителя находит по MX-записи сервер домена получателя, открывает к нему уже своё соединение и передаёт письмо, после чего оно ложится в ящик.
Дальше разобран весь путь по порядку: кто участвует в доставке, из каких команд состоит сеанс, что означают трёхзначные коды ответов, чем конверт письма отличается от заголовков, что записывается в Received и зачем домену три подписи SPF, DKIM и DMARC. Настройка отправки через посредника вынесена в отдельный материал про SMTP через прокси и выбор исходящего адреса, а разбор портов лежит в материале про почтовые порты 25, 465 и 587.
SMTP простыми словами: что берёт на себя протокол
Аббревиатура раскрывается как Simple Mail Transfer Protocol, простой протокол передачи почты. Слово «простой» здесь честное. Весь обмен состоит из строк ASCII, которые читаются глазами без декодера: клиент пишет команду, сервер отвечает трёхзначным числом и пояснением на английском. Именно поэтому почтовый сеанс до сих пор разбирают вручную через telnet или openssl s_client, и половина отказов становится понятной за пару минут наблюдения.
Протокол занят одной работой: передвинуть письмо на шаг вперёд. Он принимает письмо у отправителя и сдаёт следующему серверу по цепочке. Забор почты из ящика к SMTP отношения не имеет, этим заняты IMAP и POP3. Хранение писем, папки, флаги прочтения и поиск по ящику тоже живут вне протокола, их обслуживает почтовая система на стороне получателя.
Второе свойство важнее первого. SMTP работает по схеме push: соединение всегда открывает тот, у кого письмо на руках. Сервер получателя сидит и слушает порт, ничего сам не запрашивает и никуда не ходит. Отсюда следует практический вывод, который потом всплывает в каждом разборе доставки: адрес, с которого открыто соединение, виден принимающей стороне всегда и записывается в служебные заголовки письма. Когда отправка идёт через посредника, подходит сокетный туннель, потому что прокси SOCKS5 пропускают произвольный TCP-порт без разбора того, что внутри соединения.
Третье свойство: SMTP не гарантирует мгновенности. Сервер обязан либо принять письмо на себя и отвечать за него дальше, либо честно отказать. Приняв письмо, он ставит его в очередь и повторяет попытки доставки часами. Поэтому фраза «письмо отправлено» означает лишь то, что первый сервер взял ответственность на себя. Мы разбираем этот момент отдельно, потому что половина вопросов про доставку упирается именно в него.
Путь письма: участники доставки и четыре участка
Между кнопкой «Отправить» и папкой «Входящие» письмо проходит через четыре роли. У каждой роли есть устоявшееся сокращение, и в логах почтовых серверов эти сокращения встречаются постоянно.
| Роль | Расшифровка | Что делает | Пример программы |
|---|---|---|---|
| MUA | Mail User Agent | Пишет письмо и сдаёт его на сервер отправки | Thunderbird, The Bat, Outlook, скрипт на python |
| MSA | Mail Submission Agent | Принимает письмо от автора после авторизации, порт 587 | Postfix в режиме submission, почтовый сервис |
| MTA | Mail Transfer Agent | Ищет MX получателя и передаёт письмо дальше, порт 25 | Postfix, Exim, Sendmail |
| MDA | Mail Delivery Agent | Кладёт письмо в ящик, раскладывает по папкам | Dovecot LDA, procmail |
Первый участок это авторизованная сдача письма. Клиент соединяется со своим сервером по порту 587, представляется логином и паролем и отдаёт письмо. Здесь работает авторизация, здесь же почтовая система подставляет недостающие заголовки и присваивает письму идентификатор Message-ID.
Второй участок это межсерверная передача. Сервер отправителя выясняет, кто принимает почту для домена получателя, и открывает к нему соединение по порту 25. Авторизации на этом участке нет: любой сервер интернета имеет право принести письмо любому другому. Отсюда растут все проверки подлинности, потому что доверие приходится строить поверх открытой двери.
Третий участок это внутренняя доставка. Принимающий сервер прогоняет письмо через фильтры, сверяет подписи и передаёт агенту доставки. Четвёртый участок это уже забор письма из ящика клиентом, и он идёт по совсем другим протоколам.
Мы держим этот список ролей перед глазами при любом разборе, потому что почти каждый вопрос вида «почему письмо не дошло» решается определением участка, на котором оно остановилось.
MX-запись: как сервер отправителя находит сервер получателя
Домен получателя в адресе выглядит как example.org, при этом почту для него может принимать совсем другая машина. Связь задаётся записью MX в DNS. Сервер отправителя берёт часть адреса после знака @, запрашивает у DNS записи типа MX и получает список имён с числовыми приоритетами.
$ dig +short MX example.org
10 mx1.mailhost.example.
20 mx2.mailhost.example.
30 backup.mailhost.example.
Меньшее число означает более высокий приоритет. Сервер отправителя сначала пробует mx1, при отказе переходит к mx2 и дальше вниз по списку. Имена из ответа резолвятся в адреса записями A, и уже к ним открывается TCP-соединение. Если MX-записей у домена нет вовсе, работает запасное правило: почта идёт на адрес из записи A самого домена.
Одинаковые приоритеты у нескольких записей означают распределение нагрузки: отправитель выбирает из них произвольно. Такая конструкция часто встречается у крупных почтовых систем, где десяток машин принимает поток параллельно. Мы проверяем MX первым делом, когда письма к одному домену уходят в никуда: устаревшая запись объясняет отказ быстрее логов.
| Что запрашиваем | Тип записи | Что получаем | Зачем |
|---|---|---|---|
example.org | MX | Имена принимающих серверов с приоритетами | Определить, куда нести письмо |
mx1.mailhost.example | A | Адрес IPv4 принимающей машины | Открыть TCP-соединение |
example.org | TXT | Запись SPF со списком отправителей | Проверить право адреса на отправку |
selector._domainkey.example.org | TXT | Открытый ключ DKIM | Проверить подпись письма |
_dmarc.example.org | TXT | Политика DMARC | Понять, что делать при провале проверок |
Сама сеть, из которой уходит соединение, на выбор MX никак не влияет: список принимающих серверов одинаков для всех. Влияет она на другое, на то, какой адрес увидит принимающая сторона. Тем, кто разводит отправку по разным сетям, подходит вариант, где можно купить прокси IPv4 с доступом к общему пулу и держать соединения через него.
Сеанс SMTP по командам: от EHLO до QUIT
Сеанс устроен как диалог. Клиент отправляет одну команду строкой, сервер отвечает одной или несколькими строками с трёхзначным кодом впереди. Набор команд короткий, и рабочая отправка укладывается в шесть штук.
| Команда | Что означает | Типичный ответ |
|---|---|---|
EHLO имя | Клиент представляется и просит список расширений | 250- со списком возможностей |
HELO имя | Устаревшая форма приветствия без расширений | 250 OK |
STARTTLS | Перевести открытое соединение в шифрованное | 220 Ready to start TLS |
AUTH LOGIN | Начать авторизацию логином и паролем | 334 с запросом строки base64 |
MAIL FROM:<адрес> | Задать обратный адрес конверта | 250 Sender OK |
RCPT TO:<адрес> | Добавить получателя в конверт | 250 Recipient OK |
DATA | Начать передачу заголовков и тела | 354 End data with <CRLF>.<CRLF> |
RSET | Сбросить текущий конверт, соединение остаётся | 250 Reset state |
NOOP | Пустая команда, держит соединение живым | 250 OK |
QUIT | Закрыть сеанс корректно | 221 Bye |
Живой обмен по порту 587 выглядит так. Строки клиента помечены стрелкой, строки сервера идут без пометки.
220 mail.example.org ESMTP Postfix
> EHLO client.example.net
250-mail.example.org
250-PIPELINING
250-SIZE 26214400
250-STARTTLS
250-AUTH PLAIN LOGIN
250-8BITMIME
250 SMTPUTF8
> STARTTLS
220 2.0.0 Ready to start TLS
> EHLO client.example.net
250-mail.example.org
250-AUTH PLAIN LOGIN
250 SMTPUTF8
> AUTH LOGIN
334 VXNlcm5hbWU6
> c2VuZGVyQGV4YW1wbGUub3Jn
334 UGFzc3dvcmQ6
> cGFzc3dvcmQxMjM=
235 2.7.0 Authentication successful
> MAIL FROM:<[email protected]>
250 2.1.0 Ok
> RCPT TO:<[email protected]>
250 2.1.5 Ok
> DATA
354 End data with <CR><LF>.<CR><LF>
> From: Sender <[email protected]>
> To: User <[email protected]>
> Subject: Test message
> Date: Mon, 12 Aug 10:41:03 +0300
>
> Text of the message.
> .
250 2.0.0 Ok: queued as 4A1F2C0B7E
> QUIT
221 2.0.0 Bye
Ответ на EHLO заслуживает отдельного внимания: сервер перечисляет расширения ESMTP, и по этому списку сразу понятно, чего от него ждать. SIZE 26214400 задаёт потолок письма в байтах, около 25 мегабайт. PIPELINING разрешает слать команды пачкой без ожидания каждого ответа. 8BITMIME и SMTPUTF8 отвечают за восьмибитные тела и адреса с национальными символами. STARTTLS показывает готовность поднять шифрование поверх открытого порта. AUTH PLAIN LOGIN перечисляет доступные способы авторизации.
Разбирая чужой сеанс, мы сначала смотрим на этот перечень: он говорит про сервер больше, чем документация к нему. Тело письма завершается строкой с единственной точкой. Отсюда старое правило: строку внутри письма, которая сама состоит из точки, отправитель обязан удвоить, иначе сеанс оборвётся на середине. Библиотеки делают это сами, ручные скрипты на сокетах иногда забывают.
Коды ответов сервера: 2xx, 4xx, 5xx и что делать с временным отказом
Каждый ответ начинается с трёх цифр. Первая цифра задаёт класс и отвечает на вопрос «что дальше», вторая и третья уточняют причину. Класс читается мгновенно, и этого хватает, чтобы понять, повторять попытку или прекращать.
| Класс | Значение | Примеры | Что делает отправитель |
|---|---|---|---|
| 2xx | Команда принята и выполнена | 250 Ok, 221 Bye, 235 Authentication successful | Идёт к следующей команде |
| 3xx | Команда принята, сервер ждёт продолжения | 354 End data, 334 запрос логина | Передаёт данные, которых ждут |
| 4xx | Временный отказ, ошибка на стороне сервера | 421 Service not available, 450 Mailbox busy, 451 Local error, 452 Insufficient storage | Ставит письмо в очередь и повторяет позже |
| 5xx | Постоянный отказ, повтор бессмысленен | 550 No such user, 552 Message too large, 554 Transaction failed | Прекращает попытки и отдаёт отчёт о недоставке |
Разница между 4xx и 5xx определяет всю логику очереди. Код 450 говорит «сейчас нельзя, приходите позже», и правильно настроенный сервер отправителя тут же ставит письмо в очередь. Повторы идут по нарастающему интервалу: сначала через несколько минут, потом через полчаса, дальше через час и реже. Общий срок жизни письма в очереди у Postfix задаётся параметром maximal_queue_lifetime и по умолчанию равен пяти суткам. Всё это время отправитель продолжает стучаться.
Отдельного разбора заслуживает грейлистинг. Принимающая сторона отвечает 450 на первую попытку от незнакомой связки адреса, отправителя и получателя, запоминает её и принимает письмо со второй попытки через несколько минут. Массовые рассыльщики часто не повторяют, поэтому приём с задержкой отсекает изрядную долю мусора. Настоящий почтовый сервер повторит и доставит письмо с опозданием.
Отказ 421 означает, что сервер закрывает соединение прямо сейчас: перегрузка, перезапуск, слишком много параллельных подключений с одного адреса. Реакция та же: очередь и повтор. Смысл в том, что временный отказ обрабатывается терпением, при этом стоит смотреть на частоту таких ответов. Если 4xx приходят постоянно с одной сети, разговор идёт уже про репутацию исходящего адреса, и здесь помогают серверные адреса на собственном оборудовании с предсказуемым поведением.
Конверт письма и заголовки: почему адреса расходятся
Письмо состоит из двух слоёв, и они живут независимо. Первый слой это конверт: значения из команд MAIL FROM и RCPT TO. Второй слой это сам текст письма, который начинается после DATA и содержит заголовки From:, To:, Subject:, Date:.
Почтовые серверы маршрутизируют письмо по конверту. Заголовки внутри DATA для них обычный текст, который они передают дальше и показывают пользователю. Отсюда две вещи, которые удивляют новичков.
Первая: адрес в RCPT TO и адрес в To: совпадать не обязаны. Скрытая копия работает именно так. Получатель добавляется командой RCPT TO, при этом в заголовки его адрес не пишется, поэтому остальные адресаты его не видят. Рассылки по спискам устроены тем же приёмом: в To: стоит имя списка, в конверте перечислены реальные ящики.
Вторая: адрес в MAIL FROM и адрес в From: тоже расходятся. Конвертный отправитель отвечает за возвраты, и принимающий сервер записывает его в заголовок Return-Path при доставке. Сервисы рассылок ставят туда служебный адрес вида [email protected], чтобы отчёты о недоставке падали в автоматический обработчик. Видимый From: при этом остаётся человеческим.
| Поле | Где живёт | Кто читает | Пример значения |
|---|---|---|---|
MAIL FROM | Конверт | Почтовые серверы, проверка SPF | [email protected] |
RCPT TO | Конверт | Почтовые серверы, маршрутизация | [email protected] |
From: | Заголовки | Пользователь, проверка DMARC | Отдел продаж <[email protected]> |
To: | Заголовки | Пользователь | Клиент <[email protected]> |
Return-Path | Заголовки, ставит получатель | Обработчик возвратов | Копия значения MAIL FROM |
Расхождение этих двух слоёв породило почти все схемы подделки писем. Проверить конверт легко, потому что адрес соединения виден. Проверить заголовок From: без криптографии невозможно, ведь это просто строка текста. Три механизма, разобранные ниже, закрывают именно этот разрыв.
Заголовок Received: что по нему видно
Каждый сервер, через который прошло письмо, дописывает сверху свою строку Received. Получается стек: самая верхняя строка добавлена последним сервером, самая нижняя относится к моменту, когда письмо только сдали в отправку. Читать письмо по этим строкам нужно снизу вверх.
Received: from mx1.mailhost.example (mx1.mailhost.example [203.0.113.24])
by mail.example.com (Postfix) with ESMTPS id 4A1F2C0B7E
for <[email protected]>; Mon, 12 Aug 10:41:07 +0300
Received: from client.example.net (unknown [198.51.100.77])
by mail.example.org (Postfix) with ESMTPSA id 9D3B1A5C42
for <[email protected]>; Mon, 12 Aug 10:41:03 +0300
Нижняя строка рассказывает о сдаче письма. from client.example.net это имя, которым клиент представился в EHLO, оно задаётся программой и легко ставится любым. В скобках стоит правда: unknown означает, что обратной записи PTR у адреса нет, а 198.51.100.77 это реальный адрес, с которого пришло соединение, его подставил сам сервер. Метка ESMTPSA расшифровывается как ESMTP с шифрованием и авторизацией, буква A в конце подтверждает, что клиент прошёл AUTH.
Верхняя строка описывает межсерверную передачу: письмо принесла машина mx1.mailhost.example с адреса 203.0.113.24, приняла его mail.example.com, протокол ESMTPS без буквы A, потому что авторизации на порту 25 нет. Идентификатор очереди в поле id пригодится, когда придётся искать письмо в логах конкретного сервера.
Практический вывод простой. Адрес в самой нижней строке Received показывает, откуда физически ушло письмо. Именно его мы смотрим первым делом, когда проверяем, что отправка идёт по задуманному маршруту. Имя из EHLO при таком разборе доверия не заслуживает, потому что клиент вписывает туда что угодно. Шифрование канала на этом участке разбирается тем же способом, что и обычный прокси HTTPS с туннелем CONNECT, где посредник передаёт байты, ничего не читая.
Ещё один заголовок стоит рядом: Message-ID. Его ставит первый сервер, дальше он идёт с письмом неизменным и служит ключом поиска в логах всей цепочки. Наш обычный порядок разбора такой: сначала нижний Received, затем Message-ID, затем логи сервера отправки по этому идентификатору.
SPF, DKIM и DMARC: три проверки на стороне получателя
SPF отвечает на вопрос, имел ли адрес соединения право отправлять почту от имени домена. Владелец домена публикует запись TXT со списком разрешённых отправителей, например v=spf1 ip4:203.0.113.0/24 include:_spf.mailhost.example -all. Принимающий сервер берёт домен из конверта, из MAIL FROM, запрашивает эту запись и сверяет с ней адрес, с которого открыто соединение. Хвост -all означает жёсткий отказ всем остальным, ~all мягкую пометку. Проверка привязана к конверту, поэтому пересылка письма через промежуточный ящик её ломает: адрес соединения меняется, домен остаётся прежним.
DKIM отвечает на вопрос, менялось ли письмо в пути и подписал ли его домен. Отправляющий сервер считает подпись по телу письма и части заголовков, кладёт её в заголовок DKIM-Signature и указывает там селектор. Получатель забирает открытый ключ записью TXT по адресу селектор._domainkey.домен, пересчитывает подпись и сверяет. Ключевое отличие от SPF: проверка не зависит от адреса соединения, поэтому переживает пересылку, пока текст письма и подписанные заголовки остаются нетронутыми.
DMARC связывает первые две проверки с видимым адресом в From:. Запись публикуется в TXT по имени _dmarc.домен и выглядит как v=DMARC1; p=quarantine; rua=mailto:[email protected]; pct=100. Политика p= говорит принимающей стороне, что делать с письмом, которое провалило и SPF, и DKIM: none только наблюдать, quarantine класть в спам, reject отбивать на уровне SMTP кодом 5xx. Дополнительно DMARC требует выравнивания: домен из From: должен совпадать с доменом, который прошёл проверку. Параметр rua задаёт адрес для сводных отчётов, и они показывают владельцу домена, кто отправляет почту от его имени.
| Механизм | Что проверяет | Где лежит запись | С чем сверяется |
|---|---|---|---|
| SPF | Право адреса отправлять от имени домена | TXT на домене | Адрес соединения и домен из MAIL FROM |
| DKIM | Целостность письма и подпись домена | TXT на селектор._domainkey | Заголовок DKIM-Signature |
| DMARC | Согласование проверок с видимым From: | TXT на _dmarc | Итоги SPF и DKIM плюс выравнивание доменов |
Все три записи живут в DNS и настраиваются владельцем домена. Отправляющая сторона на них влияет косвенно: подписи ставит почтовый сервер, а адрес соединения определяется тем, откуда открыт сокет. Мы держим все три записи в одном текстовом файле рядом с настройками почтового сервера, потому что при переезде домена их правят одновременно, и забытая строка проявляется через сутки.
Как посмотреть сеанс своими руками
Ручной сеанс остаётся самым быстрым способом понять, что происходит. Для порта 587 с переходом в TLS подходит openssl s_client с ключом -starttls smtp, для порта 465 тот же инструмент без ключа, потому что там шифрование поднимается сразу.
# порт 587, шифрование поднимается командой STARTTLS
openssl s_client -connect mail.example.org:587 -starttls smtp -crlf
# порт 465, соединение шифруется с первого байта
openssl s_client -connect mail.example.org:465 -crlf
# быстрый просмотр списка расширений без ручного ввода
printf 'EHLO test.example\r\nQUIT\r\n' | nc mail.example.org 25
Утилита swaks делает то же самое одной строкой и печатает весь диалог, включая коды ответов. Она удобна, когда нужно проверить связку логина, пароля и порта, ничего не настраивая в почтовом клиенте.
swaks --to [email protected] \
--from [email protected] \
--server mail.example.org:587 \
--auth LOGIN --auth-user [email protected] \
--tls
Что мы смотрим в выводе по порядку. Первое: пришло ли приветствие 220, потому что его отсутствие означает проблему с самим соединением. Второе: какие расширения перечислены в ответе на EHLO. Третье: прошла ли авторизация, код 235 подтверждает успех. Четвёртое: приняты ли MAIL FROM и RCPT TO. Пятое: какой код пришёл после точки, завершающей DATA.
Когда отправка идёт с нескольких машин или через посредника, к этому списку добавляется шестой пункт: адрес, который увидела принимающая сторона. Он читается по нижней строке Received в доставленном письме. Схему с сокетным туннелем и разными исходящими адресами закрывает пакет с SOCKS4 и SOCKS5 на выбор, а доступ к самому пулу оформляется как приватный доступ к общему списку адресов.
Частые вопросы
Чем SMTP отличается от IMAP и POP3?
SMTP занимается только передачей письма вперёд по цепочке: от клиента на сервер и от сервера к серверу. Забор писем из ящика идёт по IMAP или POP3, это отдельные протоколы со своими портами и своей логикой. Почтовый клиент держит оба направления одновременно: по SMTP отправляет, по IMAP читает.
Почему письмо приняли с кодом 250, но оно не появилось у получателя?
Код 250 после завершения DATA означает, что сервер взял письмо в очередь и отвечает за него дальше. Что произойдёт потом, зависит от следующих участков: письмо может уйти в спам по итогам проверок DKIM и DMARC, попасть под фильтр содержимого или вернуться отчётом о недоставке. Разбор идёт по логам сервера, где письмо ищется по идентификатору очереди из ответа.
Подойдут ли ваши прокси под мою почтовую задачу?
Заранее предугадать поведение каждого почтового сервера и каждой программы нельзя, поэтому перед покупкой доступен бесплатный тест длительностью до 2 часов под ваш запрос. Тест проходит на вашем софте, и его результат отвечает на вопрос точнее любого описания. Порядок запуска короткий: выбрать тип прокси под свой софт, зарегистрироваться в кабинете, отправить запрос теста из меню, указать свой адрес в настройках, активировать доступ и написать оператору логин и тип прокси.
Какой тип прокси вы даёте для работы с почтовыми портами?
В пакете IPv4 и SOCKS5 доступны SOCKS4 и SOCKS5 на выбор, рекомендуем SOCKS5. Пул держится в районе 12 000 активных адресов, список обновляется в реальном времени и выдаётся в форматах IP:PORT и IP:PORT:LOGIN:PASS ссылкой или файлом. Трафик безлимитный, пакет включается примерно за 5 минут после оформления.
Соседние разборы по протоколам собраны рядом: приём почты по IMAP и POP3 с разбором папок и флагов, туннель CONNECT для шифрованных соединений и сравнение вариантов в материале HTTP, HTTPS, SOCKS4 и SOCKS5. Практическая настройка почтовых программ и серверных скриптов описана отдельно в разделе про подключение в программах и на сервере.