Рано или поздно каждая команда случайно отправляет письма настоящим клиентам: в staging попал боевой конфиг, в цикле остался реальный адрес, крон, который должен был быть отключён. Жалобы и баунсы бьют по репутации отправляющего домена, и кнопки «отменить» здесь нет.
Песочница для почты убирает этот риск: она принимает письмо по SMTP, сохраняет его и показывает в дашборде. Она никогда не пересылает и не доставляет письма и принимает любого получателя.
Что песочница делает, а что нет
- Принимает SMTP на порту из вашего инбокса, с выданными логином и паролем.
- Разбирает письмо: тему, From/To/Cc/Bcc, конверты, все заголовки, текст, HTML и вложения.
- Хранит его заданный срок и удаляет автоматически.
- Никуда не отправляет: очереди и релея нет, поэтому даже адрес реального клиента останется внутри песочницы.
Шаг 1. Создайте инбокс и сохраните креды
Зарегистрируйтесь, создайте инбокс и откройте его. Вы получите хост, порт, логин и пароль. Пароль показывается один раз — сохраните его сразу; при перегенерации старый перестанет работать.
Шаг 2. Направьте на песочницу своё приложение
Laravel (.env):
MAIL_MAILER=smtp
MAIL_HOST=sandbox-host
MAIL_PORT=2525
MAIL_USERNAME=inbox_xxxxxxxx
MAIL_PASSWORD=скопированный-пароль
MAIL_ENCRYPTION=null
Python:
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["Subject"] = "Привет из песочницы"
msg["From"] = "app@example.com"
msg["To"] = "anyone@example.com"
msg.set_content("Проверка тестового сценария")
with smtplib.SMTP("sandbox-host", 2525) as server:
server.login("inbox_xxxxxxxx", "скопированный-пароль")
server.send_message(msg)
Любой SMTP-клиент работает так же: Symfony Mailer, Django, nodemailer, swaks, msmtp, плагины WordPress или устройство, которое умеет только SMTP.
Шаг 3. Посмотрите результат
Отправьте письмо и откройте инбокс: оно появится сразу — с отрендеренным HTML (показывается в изолированном iframe со отключёнными скриптами), текстовой частью, всеми заголовками и вложениями. Доставки не было, поэтому один и тот же сценарий можно прогнать сотню раз.
Шаг 4. Проверьте то, что обычно ломается в продакшене
- Ссылки и картинки должны быть абсолютными. Относительные пути локально работают, а в реальном клиенте — нет.
- Ссылка на отписку и физический адрес обязательны для рассылок.
- Reply-To должен указывать на живую почту, а не на
noreply@.
- Кодировка темы для кириллицы и эмодзи.
- Вложения: лимиты размера, корректные content-type, имена файлов без пробелов.
- SPF, DKIM и DMARC боевого отправителя — песочница их не проверит, но покажет точные заголовки, которые формирует ваше приложение.
Подсказка: автоматизируйте
В интеграционных тестах проверяйте перехваченное письмо, а не реальный ящик: отправьте через песочницу, затем прочитайте его обратно (у read-only ссылки есть экспорт в JSON) и проверьте тему, получателей и тело. Тест останется быстрым, детерминированным и безопасным.
Что дальше
Письма точно не уйдут получателям?
Да. Сервер принимает письмо, сохраняет его и показывает в дашборде. Пересылки и доставки нет: любой адрес получателя принимается и отбрасывается.
Подходит ли для автотестов?
Да. Укажите в тестовом окружении хост и порт песочницы и проверяйте перехваченное письмо (у ссылки шаринга есть JSON-экспорт) вместо реальной отправки.
Чем подмена мейлера отличается от песочницы?
Подмена доказывает, что код вызвал отправку; песочница показывает само письмо — заголовки, HTML, текстовую часть и вложения. Нужны оба: подмена в юнит-тестах, песочница в интеграционных.
Нужен ли Docker для тестового SMTP-сервера?
Не обязательно. Этот инстанс — готовый хост, который вы только настраиваете, а Mailpit и smtp4dev — контейнеры или отдельные бинарники, которые вы запускаете сами. Для приложения они одинаковы: хост и порт.
Какой порт и шифрование использовать?
Те, что показаны на странице инбокса: песочница принимает обычный SMTP, STARTTLS и неявный TLS на своих портах, поэтому в конфиге приложения особых случаев не нужно.
Перехваченные письма когда-нибудь удаляются?
Да. Письма автоматически удаляются по истечении настроенного срока хранения, а вложения — по своему короткому расписанию.
Попробуйте сами
Создайте бесплатный аккаунт, сделайте инбокс или URL вебхука и направьте на них своё приложение. Ничего не доставляется реальным получателям.
Четыре способа тестировать вебхуки локально: постоянный catch-all URL, туннель, повторная доставка от провайдера и локальная ловушка — плюс разбор запроса и проверка подписи.
Что использовать вместо Mailtrap: Mailpit, smtp4dev и заброшенный MailHog в сравнении с приватно хостируемой песочницей, плюс чек-лист переезда за десять минут.
Хватит вставлять скриншоты в чат: та же read-only ссылка отдаёт JSON или простой текст, чтобы модель проверила тему, заголовки, ссылки и тело каждого письма.
MailHog не поддерживается уже несколько лет. Чем его заменить (Mailpit, smtp4dev, приватно хостируемая песочница), как переехать за пять минут и что вы при этом теряете.
Mailpit вы запускаете сами, MailHog — то, что он заменил, Mailtrap — сервис по подписке: сравнение по обслуживанию, цене, месту хранения писем и командным функциям.
Что такое mailtrap.io, чем ограничен его бесплатный тариф по счётчику, что означают запросы «mail trap», «trap email» и «mailtrap login» — и какие есть бесплатные альтернативы.
Сервис Mailpit для вашего compose-файла: порты, том с данными чтобы письма переживали перезапуск, конфигурации приложений, healthcheck для CI и ошибки, которые стоит знать заранее.
Mail::fake() доказывает, что код вызвал отправку; настоящая SMTP-песочница доказывает, что письмо правильное. Обе настройки, стили проверок, ловушка с очередью и ошибки, из-за которых сломанное письмо уходит в прод.
На что смотреть, когда перехваченные payload-ы настоящие: отзыв доступа, noindex, история запросов, read-only шаринг и машинный экспорт — и как на каждый пункт отвечает этот инстанс.