Что такое адрес возврата?
Адрес возврата, или bounce address, — адрес, на который почтовая система сообщает об отказе доставки. В SMTP он связан с envelope sender и обычно передаётся в команде MAIL FROM. Он может отличаться от видимого заголовка From, который видит получатель.
Зачем он нужен
Если письмо нельзя доставить, принимающий сервер формирует уведомление о недоставке, или bounce. По нему отправляющая система узнаёт о временной или постоянной ошибке, очищает неактивные адреса и не продолжает бесполезные попытки. Отсутствие корректного адреса затрудняет обработку отказов.
From: John Doe <[email protected]>
MAIL FROM: <[email protected]>
Отличие от From
From является пользовательским заголовком и может быть подделан, тогда как envelope sender участвует в SMTP-диалоге и проверках SPF, DMARC и обратной связи. Reply-To задаёт адрес ответов и тоже не обязан совпадать с bounce address. Смешение этих полей часто приводит к неверной атрибуции.
Client: MAIL FROM: <[email protected]>
Server: 250 OK
Client: RCPT TO: <[email protected]>
Server: 550 No such user here
Client: QUIT
# Later, server sends bounce to [email protected]
Типы отказов
Временный отказ 4xx может привести к повторной попытке, а постоянный 5xx обычно требует удаления или исправления адреса. Ошибка DNS, недоступный MX, переполненный ящик, политика получателя и блокировка имеют разный смысл. Код ответа нужно сохранять вместе с доменом, временем и источником.
Аутентификация
Домен возврата должен быть согласован с SPF, DKIM и DMARC-политикой отправителя. Участие bounce-домена в проверке зависит от alignment и используемого поля From. CNAME, forwarding и внешний ESP не отменяют необходимости документировать, кто реально отправляет сообщения.
Персональные данные
Уведомления о недоставке могут содержать адрес получателя, тему, фрагменты заголовков и диагностический текст. Ограничивайте срок хранения, доступ и передачу таких данных, особенно в логах и аналитике. Не публикуйте реальные адреса в примерах.
Пересылка
Автоматическая пересылка может изменить envelope sender, сломать SPF или скрыть исходную причину отказа. SRS и корректная обработка DSN помогают сохранить обратный маршрут, но зависят от почтового провайдера. Проверяйте реальный SMTP-путь, а не только DNS.
Мониторинг
Следите за долей bounce, кодами 4xx и 5xx, изменениями MX, блокировками и жалобами. Резкий рост отказов может означать плохой список, ошибку конфигурации или репутационную проблему. Снимок одного ответа не доказывает общую доставляемость.
Проверка
Проверяйте конверт отправителя, поля From и Reply-To, SPF, DKIM, DMARC, MX, TLS и фактический ответ SMTP. Тайм-аут, 401, 403, 429 и сетевой сбой классифицируйте как неизвестный результат. Не считайте наличие bounce address доказательством личности отправителя или безопасности почты.
$to = "[email protected]";
$subject = "Test Email";
$message = "Email body";
$headers = "From: [email protected]\r\n";
$headers .= "Return-Path: [email protected]\r\n";
mail($to, $subject, $message, $headers, "-f [email protected]");
$mail = new PHPMailer();
$mail->From = "[email protected]";
$mail->Sender = "[email protected]"; // Return path
$mail->addAddress("[email protected]");
$mail->Subject = "Test Email";
$mail->send();
# /etc/postfix/main.cf
sender_canonical_maps = hash:/etc/postfix/sender_canonical
# /etc/postfix/sender_canonical
@example.com [email protected]
# Apply changes
postmap /etc/postfix/sender_canonical
systemctl reload postfix
Рекомендации
Используйте стабильный домен возврата, ограничивайте его назначение, защищайте доступ к журналам и обрабатывайте отказ идемпотентно. Перед изменением DNS учитывайте TTL и кэш. Адрес возврата является частью почтового маршрута, но не заменяет полный анализ домена и политики отправки.
{
"Message": {
"Subject": "Test",
"From": "[email protected]",
"ReturnPath": "[email protected]"
}
}
const msg = {
to: '[email protected]',
from: '[email protected]',
replyTo: '[email protected]',
return_path: '[email protected]',
subject: 'Test Email',
text: 'Email body'
};
Поля SMTP
Envelope sender участвует в MAIL FROM и обратном маршруте, From отображается пользователю, а Reply-To задаёт адрес ответа. Эти поля могут различаться, и каждое нужно проверять отдельно. SPF относится к envelope domain, а DMARC учитывает alignment с видимым From.
Обработка DSN
Сохраняйте код, домен, время, тип временной или постоянной ошибки и источник, затем применяйте безопасную дедупликацию. Не отправляйте повторно адреса с постоянным отказом без оснований. Диагностический текст может содержать персональные данные и должен быть ограничен.
[email protected] # General bounces
[email protected] # No-reply emails
[email protected] # Returns/receipts
Реальная проверка
Сверяйте адрес возврата с MX, SPF, DKIM, DMARC, TLS, пересылкой и фактическим путём SMTP. Наличие адреса не доказывает, что он принимает сообщения, принадлежит отправителю или обеспечивает доставляемость.
[email protected]
Как работает адрес возврата
Bounce address принимает сообщения о недоставке, временной ошибке или отказе. Он может отличаться от видимого адреса From и задаётся envelope sender, Return-Path или сервисом рассылки. По этому адресу отправитель получает диагностический код SMTP, но успешное принятие bounce не означает, что исходное письмо достигло входящих.
Sending to: [email protected]
Return path: [email protected]
When bounce arrives at bounces+*, parse to identify failed recipient
Обработка отказов
Разделяйте hard bounce, soft bounce, блокировку, rate limit и жалобу на спам. Постоянный отказ требует удалить адрес или исправить домен, а временный нужно повторить с ограничением скорости. Сохраняйте код, timestamp, домен назначения и безопасную категорию ошибки, не записывая лишние персональные данные.
Репутация
Высокая доля возвратов ухудшает репутацию IP и домена. Поддерживайте чистый список, обрабатывайте отписки, проверяйте MX, SPF, DKIM и DMARC, а при изменении провайдера постепенно наращивайте объём. Нельзя считать любой принятый SMTP-код доказательством доставки.
import email
from email import policy
def parse_bounce(raw_email):
msg = email.message_from_string(raw_email, policy=policy.default)
# Extract bounce type
if "550" in msg.get_payload():
return "hard_bounce"
elif "452" in msg.get_payload():
return "soft_bounce"
# Extract failed recipient
for part in msg.walk():
if part.get_content_type() == "message/delivery-status":
# Parse delivery status
pass
return bounce_info
# Integrate with mailing list to remove hard bounces
Диагностика
Сопоставляйте bounce с заголовками сообщения, очередью, MX и политикой принимающего сервера. Если уведомление не пришло, это может означать потерю, фильтрацию или неправильный Return-Path. При расследовании проверяйте изолированно и не отправляйте повторно чувствительные данные на неизвестный адрес.
POST /bounce-webhook
{
"email": "[email protected]",
"event": "bounce",
"reason": "550 5.1.1 User unknown",
"type": "blocked",
"status": "5.0.0"
}
Результат проверки
Сопоставляйте envelope sender с MX, SPF, DKIM, DMARC, forwarding и фактическим SMTP-ответом. Тайм-аут и временный отказ не доказывают недоставку навсегда, а существование bounce address не подтверждает личность отправителя.
Message:
From: [email protected] (header)
Return-Path: [email protected] (envelope)
SPF Check:
Queries: mail-server.com TXT record (not example.com)
Must include sending IP in mail-server.com's SPF
Пример SMTP-диалога
Envelope sender передаётся в MAIL FROM, а после постоянного отказа принимающая система может сформировать DSN на этот адрес. Видимый From при этом не обязан совпадать с Return-Path.
bounces.example.com. IN TXT "v=spf1 include:_spf.sendgrid.net ~all"
Постоянные отказы
Hard bounce обычно означает несуществующий ящик, недопустимый домен или окончательный запрет. Такой адрес исключают из повторных отправок после подтверждённого 5xx и сохраняют безопасную категорию причины.
Временные отказы
Soft bounce связан с переполненным ящиком, временной недоступностью, rate limit или transient policy. Повтор выполняют с ограниченным backoff и прекращают после установленного числа попыток.
# Bad
Return-Path: [email protected]
# Good
Return-Path: [email protected]
Отказы из-за блокировки
Отказ из-за блокировки возникает из-за репутации, политики содержимого, аутентификации или списка блокировки. Он требует анализа точного SMTP-кода и политики получателя, а не автоматического удаления действующего адреса.
| Доля отказов | Оценка | Действие |
|---|---|---|
| < 2% | Норма | Продолжать мониторинг |
| 2–5% | Требует внимания | Проверить качество списка |
| 5–10% | Плохо | Немедленно очистить список |
| > 10% | Критично | Доставляемость под угрозой |
Настройка Return-Path
Return-Path формируется из envelope sender на стороне доставки и не должен просто копироваться как произвольный пользовательский заголовок. Настройте bounce-domain в системе отправки и проверьте фактическое полученное письмо.
-- Mark emails with hard bounces
UPDATE mailing_list
SET status = 'bounced', bounce_count = bounce_count + 1
WHERE email IN (SELECT email FROM recent_hard_bounces);
-- Remove after 3 hard bounces
DELETE FROM mailing_list
WHERE bounce_count >= 3;
Выделенные сервисы обработки bounce
Почтовая платформа может принимать DSN, классифицировать код и отправлять webhook. Договор должен определять retry, дедупликацию, срок хранения, privacy и поведение при недоступности callback.
[email protected] # Order confirmations, receipts
[email protected] # Newsletters, campaigns
Выделенный поддомен
Bounce удобно размещать на поддомене вроде bounce.example.com, отделяя его SPF и MX от пользовательской почты. Поддомен должен оставаться под контролем и иметь документированного владельца.
#!/bin/bash
# Process bounces every hour
# Fetch bounces from IMAP
fetchmail -c /etc/fetchmailrc
# Parse and update database
/usr/local/bin/process-bounces.py
# Clean up processed bounces older than 30 days
find /var/mail/bounces -mtime +30 -delete
Адреса для отдельных кампаний
Отдельный local part или subdomain помогает связать отказ с кампанией и типом сообщения. Идентификатор не должен раскрывать персональные данные или позволять угадать чужую активность.
Переменный конверт отправителя (VERP)
VERP кодирует получателя или message identifier в уникальном bounce address, упрощая сопоставление DSN. Значение нужно подписывать или проверять, чтобы поддельный адрес не менял чужой статус.
1. Spammer sends email with forged From
2. Your server accepts it
3. Your server realizes it's spam/invalid
4. Your server bounces to forged From address
5. Innocent party receives bounce (backscatter)
# Postfix: Reject unknown users at SMTP time
smtpd_recipient_restrictions = reject_unauth_destination
local_recipient_maps = hash:/etc/postfix/local_recipients
Автоматический разбор bounce
Парсер должен учитывать MIME, DSN-поля, исходный SMTP-код и неодинаковый текст серверов. Нераспознанное сообщение остаётся unknown и не должно автоматически блокировать адрес.
Обработка через webhook
Webhook проверяют подписью, обрабатывают идемпотентно и сохраняют provider event ID. Повтор события не должен увеличивать счётчик или повторно отключать получателя.
SPF для bounce-домена
SPF проверяется для envelope domain, поэтому bounce-subdomain должен разрешать реальные серверы отправки и соблюдать лимит DNS lookup. DMARC alignment оценивается отдельно относительно видимого From.
Выделенный адрес возврата
Не используйте личный ящик сотрудника как общий Return-Path. Выделенный адрес упрощает права, мониторинг, ротацию и сохранение диагностических данных без смешения с обычной перепиской.
Разделение транзакционных и маркетинговых отказов
Разные потоки имеют разные ожидания и политики повторов. Отдельные bounce streams помогают не блокировать критичное уведомление из-за качества маркетингового списка.
Проблема backscatter
Если злоумышленник подделал чужой sender, автоматический bounce на невиновный адрес создаёт backscatter. Не отправляйте DSN после принятия подозрительного сообщения, если отказ можно вернуть во время SMTP-сессии.
Подделка bounce
Фальшивое уведомление может содержать вредоносную ссылку или заставить систему удалить действующего получателя. Проверяйте источник, correlation ID, исходное сообщение и подпись webhook до изменения состояния.
# Test bounce to invalid address
swaks --to [email protected] \
--from [email protected] \
--server mx.test-domain.com
# Check if bounce arrives at [email protected]
dig bounces.example.com TXT
# Should show SPF record with authorized senders