Отправка почты из Yii 2: разбираем на живом примере
Форма обратной связи на сайте молчит, письма не доходят или падают в спам. Разбираем настройку компонента mailer в Yii 2: SMTP, шаблоны, вложения и очередь.

Задача звучит просто: пользователь заполнил форму, вам на почту падает письмо. В Yii 2 за это отвечает компонент mailer и библиотека yiisoft/yii2-swiftmailer (либо yii2-symfonymailer в свежих сборках). Проблема начинается, когда вы копируете чей-то конфиг из интернета, письмо уходит через mail() и оседает в спаме, а на проде вообще ничего не приходит без единой ошибки в логе. Разберём, как собрать отправку через SMTP так, чтобы письма доходили, а форма не подвешивала пользователя на две секунды.
Что настраиваем и почему транспорт по умолчанию не подходит
По умолчанию mailer в Yii 2 использует PHP-функцию mail(). Это транспорт, при котором вы не контролируете почти ничего: письмо уходит от локального MTA сервера, часто без корректного SPF, и почтовики вроде Gmail и Mail.ru отправляют его в спам или молча отбрасывают. Для рабочего проекта нужен SMTP через реальный почтовый аккаунт или транзакционный сервис.
Второй момент: библиотека transport. В Yii 2 исторически использовался SwiftMailer, но сам SwiftMailer официально заброшен1. В новых проектах ставят yiisoft/yii2-symfonymailer, который построен на Symfony Mailer. Синтаксис конфига почти совпадает, но класс компонента другой. Ниже примеры для symfonymailer как для актуального варианта, отличия для swiftmailer отмечаю отдельно.
Если у вас старый проект на swiftmailer — он продолжит работать, но новых заплаток безопасности для него не выпускают. Планируйте переезд на symfonymailer, пока это не стало срочным.
SMTP versus транзакционный сервис
Отправлять напрямую через SMTP своего почтового ящика можно, но у бесплатных ящиков есть лимиты и риск блокировки за рассылку. Для форм обратной связи это терпимо. Для регистраций, восстановления пароля и уведомлений берут транзакционный сервис — у него готовый SPF/DKIM и доставляемость выше.
| Вариант | Что выигрываете | Чем платите |
|---|---|---|
mail() | Ноль настройки | Спам, нет контроля, тихие потери |
| SMTP личного ящика | Просто, бесплатно | Лимиты, риск блокировки, ручной DKIM |
| Транзакционный сервис | Доставляемость, статистика, готовый DKIM | Цена при больших объёмах, регистрация домена |
Конфиг компонента mailer
Настройки лежат в config/web.php (или в общем конфиге для advanced-шаблона), в секции components. Реальные логин и пароль в репозиторий не кладут — выносите их в .env или в незакоммиченный params-local.php.
// config/web.php — Yii 2, yii2-symfonymailer
'components' => [
'mailer' => [
'class' => \yii\symfonymailer\Mailer::class,
// адрес отправителя по умолчанию для всех писем
'messageClass' => \yii\symfonymailer\Message::class,
'transport' => [
'scheme' => 'smtps',
'host' => 'smtp.example.com',
'username' => getenv('MAIL_USER'),
'password' => getenv('MAIL_PASS'),
'port' => 465,
],
// useFileTransport: письма не уходят, а пишутся в файл — для локальной отладки
'useFileTransport' => false,
],
],
Для старого swiftmailer класс будет yii\swiftmailer\Mailer, а внутри transport вместо scheme используется encryption со значением ssl или tls. Не смешивайте эти два синтаксиса — при неверном ключе транспорт молча падает на дефолтный.
Порт 465 — это SMTP over SSL (smtps). Порт 587 — STARTTLS, для него scheme ставят smtp, а шифрование поднимается уже внутри соединения. Какой порт открыт — смотрите в панели вашего почтового провайдера, они различаются.
Отправка письма из контроллера
Минимальный рабочий вызов на живом примере — форма обратной связи, которая шлёт письмо администратору:
// controllers/SiteController.php
public function actionFeedback()
{
$name = 'Иван';
$userEmail = 'ivan@example.com';
$text = 'Сообщение из формы';
$sent = Yii::$app->mailer->compose()
->setFrom(['noreply@example.com' => 'Сайт компании'])
->setTo('admin@example.com')
// replyTo, чтобы ответить сразу пользователю
->setReplyTo([$userEmail => $name])
->setSubject('Новое сообщение с сайта')
->setTextBody($text)
->send();
if ($sent) {
Yii::$app->session->setFlash('success', 'Отправлено');
} else {
Yii::error('Письмо не ушло', 'mail');
Yii::$app->session->setFlash('error', 'Ошибка отправки');
}
return $this->refresh();
}
Ключевой момент — setFrom должен указывать домен, для которого настроен SPF, а не почту пользователя. Если поставить setFrom($userEmail), почтовик увидит несовпадение отправителя и SPF-записи вашего сервера и отправит письмо в спам. Реальный адрес пользователя кладите в setReplyTo — тогда кнопка «Ответить» в вашем ящике пишет ему напрямую.
Шаблон письма вместо строки
Собирать HTML руками в контроллере неудобно. Yii умеет рендерить письмо из view-файла. Шаблоны по умолчанию лежат в @app/mail/. Создайте mail/feedback.php с обычной Yii-разметкой и вызывайте так:
Yii::$app->mailer->compose(
// html-шаблон и текстовый fallback
['html' => 'feedback-html', 'text' => 'feedback-text'],
['name' => $name, 'text' => $text]
)
->setFrom(['noreply@example.com' => 'Сайт'])
->setTo('admin@example.com')
->setSubject('Сообщение с сайта')
->send();
Второй аргумент compose() — массив переменных, доступных внутри шаблона. Всегда давайте и HTML, и text-версию: часть почтовиков и умные часы показывают именно текстовую часть, а письмо без неё выглядит подозрительно для спам-фильтров.
Вложения и картинки
Файл к письму цепляют методом attach(), содержимое из строки — attachContent(). Встроенные в HTML картинки — embed(), он возвращает CID для тега img:
$message = Yii::$app->mailer->compose('report', ['data' => $data]);
$imageCid = $message->embed('@app/web/images/logo.png');
// внутри шаблона report.php: <img src="<?= $imageCid ?>">
$message->attach('@runtime/report.pdf', ['fileName' => 'Отчёт.pdf']);
$message->send();
Помните про размер: письма тяжелее нескольких мегабайт часть провайдеров режет. Крупные файлы лучше отдавать ссылкой, а не вложением.
Почему форма подвисает и что с очередью
Синхронный send() в контроллере блокирует ответ пользователю на всё время соединения с SMTP — обычно от полусекунды до нескольких секунд, а при недоступном SMTP форма висит до таймаута. На проде отправку выносят в фон.
Готового встроенного драйвера очереди в mailer нет — используют расширение yiisoft/yii2-queue. Схема такая: контроллер кладёт в очередь job с данными письма, а воркер очереди уже вызывает send(). Настройка самой очереди — отдельная тема, здесь важно понять принцип: контроллер не должен ждать SMTP.
- Быстрый ответ пользователю — форма не висит на таймауте SMTP.
- Ретраи при сбое — очередь повторит job, а не потеряет письмо.
- Пиковую нагрузку сглаживает воркер, а не веб-процесс.
Если не получилось
Типичные симптомы и что за ними стоит:
send()вернулfalse, в логе тишина. ВключитеuseFileTransport => true— если файл в@runtime/mailпоявился, значит проблема в SMTP, а не в коде. Проверьте логин, пароль и порт.- Connection could not be established / Connection timed out. Хостинг блокирует исходящий порт 25/465/587. Уточните у поддержки, какой порт открыт, и смените его в конфиге.
- Authentication failed / 535. Неверный пароль либо провайдер требует пароль приложения вместо основного (Gmail, Яндекс). Сгенерируйте отдельный пароль приложения в настройках почты.
- Письмо уходит, но падает в спам. Проверяйте не код, а DNS: настройте SPF и DKIM для домена отправителя. Без них доставляемость низкая независимо от Yii.
- Кириллица в теме превратилась в кракозябры. Убедитесь, что файлы в UTF-8 и что вы не переопределяли кодировку сообщения вручную.
Что настроить дальше
Когда базовая отправка работает — займитесь доставляемостью и надёжностью. По порядку: настройте SPF и DKIM в DNS домена и проверьте их через mail-tester или аналог; вынесите отправку в yii2-queue, чтобы форма не висела; добавьте логирование ответов SMTP в отдельную категорию, чтобы на проде видеть отказы. Точную версию и API компонента сверяйте по README расширения — детали методов между релизами symfonymailer и swiftmailer различаются.
Мотивационное письмо и эссе: Поступление, стипендии, гранты, обмен
Спросить за 25 ₽Источники: Yii 2 Guide: Sending Emails, yiisoft/yii2-symfonymailer (GitHub)
Частые вопросы
У меня старый проект на yii2-swiftmailer, всё переписывать?
Письма уходят, но все попадают в спам. Дело в Yii?
Как проверить настройку локально, не отправляя реальных писем?
Форма обратной связи тормозит на несколько секунд. Что делать?
Использую Gmail, аутентификация падает с ошибкой 535. Почему?
На хостинге письма вообще не уходят, а локально всё работает. В чём разница?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.