Письма в WordPress: как добиться, чтобы они доходили
Форма обратной связи отправляет письмо, wp_mail() возвращает true, а в ящике ничего нет. Разбираем четыре способа отправки из WordPress, цену каждого и места, где всё ломается незаметно.

Клиент оформил заказ, письмо с подтверждением не пришло. В логах чисто, плагин формы показывает «отправлено», хостер говорит «у нас всё работает». Почти всегда причина одна: WordPress по умолчанию отдаёт письмо в системную функцию PHP mail(), та передаёт его локальному сендмейлу на шаред-хостинге, а Gmail и Mail.ru такие письма либо кладут в спам, либо отбрасывают молча. Привычное решение — «поставить плагин SMTP» — закрывает половину проблемы и создаёт новые: висящую админку, пароли в базе и полное отсутствие информации о том, что письмо отбили. Ниже — четыре рабочих подхода и чем каждый платит. Актуально для WordPress 6.x на PHP 8.1 и выше.
Что именно делает wp_mail()
Функция wp_mail( $to, $subject, $message, $headers, $attachments ) — не самостоятельный почтовик, а обёртка над библиотекой PHPMailer, которая лежит в ядре. Начиная с WordPress 5.5 это PHPMailer 6.x в своём пространстве имён. Ядро настраивает PHPMailer на режим mail() и даёт вам крючки, чтобы это поменять.
Три вещи, которые ломают доставку ещё до выхода письма с сервера:
- Адрес отправителя. По умолчанию это
wordpress@ваш-домен, и часто домен не тот, что в адресной строке (например, с префиксом сервера). Для приёмной стороны это письмо от имени домена, который его не авторизовал. - Молчаливый отказ.
wp_mail()возвращаетfalseбез подробностей. Причина уходит в экшенwp_mail_failed— если его никто не слушает, она теряется. - Синхронность. Отправка идёт в том же запросе, что и действие пользователя. SMTP-сервер отвечает за две секунды — покупатель две секунды смотрит на спиннер.
Минимум, который стоит поставить на любой сайт до всех остальных решений, — mu-плагин с внятным отправителем и логированием отказов:
<?php
// wp-content/mu-plugins/mail-basics.php
// WordPress 6.x, PHP 8.1+
// Причина отказа в debug.log вместо молчаливого false
add_action( 'wp_mail_failed', function ( $error ) {
// $error — объект WP_Error, в данных лежит контекст письма
error_log( 'wp_mail failed: ' . $error->get_error_message() );
} );
// Отправитель на том домене, который подписан DKIM
add_filter( 'wp_mail_from', function () {
return 'robot@example.com';
} );
add_filter( 'wp_mail_from_name', function () {
return 'Магазин «Пример»';
} );
Чтобы логи вообще писались, в wp-config.php нужны WP_DEBUG и WP_DEBUG_LOG со выключенным WP_DEBUG_DISPLAY. На проде это безопасно: вывод идёт в файл, не на страницу.
Четыре подхода к отправке
1. Системный mail() как есть
Работает ровно в одном случае: сайт живёт на том же сервере, что и почта домена, у хостера настроены DKIM и SPF, писем мало. Такое бывает у хостеров, которые продают хостинг и почту одним пакетом. Проверяется за минуту: отправьте тестовое письмо на адрес из mail-tester.com и посмотрите оценку. Если 9 и выше — не трогайте то, что работает.
2. SMTP через плагин
WP Mail SMTP, FluentSMTP, Post SMTP — все перехватывают отправку и уводят её на внешний SMTP-сервер: ваш корпоративный ящик, Яндекс 360, почтовый сервис рассылок. Настройка — через интерфейс, кода не требуется. Разница между плагинами в основном в логах: Post SMTP и FluentSMTP умеют хранить отправленные письма и уведомлять о сбоях, базовая версия WP Mail SMTP ведёт учёт скромнее.
Главный подводный камень — пароль. Многие плагины умеют читать его из констант, и это предпочтительнее хранения в базе: дамп базы уезжает к подрядчику вместе с доступом к вашей почте.
3. Свой обработчик phpmailer_init
Если плагин не нужен, пять строк в mu-плагине делают то же самое. Экшен phpmailer_init получает объект PHPMailer по ссылке, дальше работает обычное API библиотеки:
<?php
// wp-content/mu-plugins/smtp.php
add_action( 'phpmailer_init', function ( $phpmailer ) {
$phpmailer->isSMTP();
$phpmailer->Host = 'smtp.example-provider.com';
$phpmailer->Port = 587; // 25 у большинства хостеров закрыт
$phpmailer->SMTPAuth = true;
$phpmailer->SMTPSecure = 'tls'; // 'ssl' для порта 465
$phpmailer->Username = SMTP_USER; // константы из wp-config.php
$phpmailer->Password = SMTP_PASSWORD; // YOUR_SMTP_PASSWORD, не в коде темы
$phpmailer->Timeout = 10; // чтобы админка не висела минутами
} );
Точные имена свойств PHPMailer сверяйте с документацией той версии, что идёт с вашим WordPress: файл лежит в wp-includes/PHPMailer/. Когда нужно увидеть диалог с сервером, в том же обработчике выставляются SMTPDebug и Debugoutput = 'error_log' — и сразу убираются после отладки, иначе логи распухнут.
4. HTTP API почтового сервиса
Вместо SMTP письмо уходит обычным POST-запросом в API провайдера. Это быстрее (один HTTP-запрос против многошагового SMTP-диалога), не упирается в закрытые порты и возвращает идентификатор письма, по которому потом видно статус: доставлено, отбито, помечено спамом. В WordPress такой режим обычно включает официальный плагин провайдера или FluentSMTP, где API-ключ вводится в настройках.
Если пишете сами, короткий путь — фильтр pre_wp_mail (добавлен в WordPress 5.7): он позволяет полностью подменить отправку, вернув true вместо работы ядра. На ветках старше 5.7 его нет, проверяйте по справочнику функций.
| Подход | Настройка | Что выигрываете | Чем платите |
|---|---|---|---|
| mail() как есть | ничего | нулевая сложность | доставка зависит от хостера, статусов нет |
| SMTP через плагин | 10 минут в админке | подпись DKIM провайдера, логи писем | плюс один плагин, пароль в базе, задержка запроса |
| phpmailer_init своими руками | mu-плагин, 10 строк | нет лишнего кода, секреты в wp-config | логов и отчётов нет, настраивать некому кроме вас |
| HTTP API сервиса | плагин провайдера или фильтр | скорость, bounce-статусы, аналитика | тариф, зависимость от внешнего сервиса |
Когда что выбирать
Критерий простой — объём и цена потерянного письма.
- До 50 писем в сутки, письма технические (уведомления о комментариях, сброс пароля): SMTP корпоративного ящика через плагин. Лимиты на исходящие у Яндекс 360 и Mail.ru для бизнеса есть, конкретные цифры смотрите в тарифах — они меняются.
- Магазин на WooCommerce, письма несут деньги: транзакционный сервис по API. Потерянное письмо с трек-номером стоит дороже подписки.
- Сайт на поддержке у вас одного, плагинов и так тридцать:
phpmailer_initв mu-плагине плюс логированиеwp_mail_failed. - Рассылки по базе подписчиков: не через
wp_mail()вообще. Это отдельный класс задач с отписками и репутацией домена, здесь рассматриваем только транзакционные письма*.
Письмо доходит не потому, что код отправки правильный, а потому что принимающая сторона верит вашему домену. Код — это последние пять процентов работы.
Что ломается
DNS без подписи
Без SPF, DKIM и DMARC письмо выглядит подозрительно независимо от способа отправки. Три записи в зоне домена, значения берутся в панели почтового провайдера:
example.com. TXT "v=spf1 include:spf.provider.example ~all"
mail._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=ОТКРЫТЫЙ_КЛЮЧ_ОТ_ПРОВАЙДЕРА"
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
SPF-запись у домена должна быть одна: две записи v=spf1 дают ошибку проверки. Gmail с 2024 года для отправителей от 5000 писем в сутки требует и SPF, и DKIM, и DMARC, плюс держит порог жалоб на спам ниже 0,3% — актуальные требования смотрите в справке Google для отправителей.
Админка висит на отправке
Классика: массовое обновление статусов заказов, на каждый заказ письмо, каждое по две секунды. Лечится асинхронной отправкой. Если на сайте есть WooCommerce, вместе с ним стоит библиотека Action Scheduler — она пишет задания в таблицы базы, а не в опции, и переваривает очереди лучше, чем встроенный WP-Cron. Функции для постановки задания (as_enqueue_async_action и родственные) описаны в её документации, имена сверяйте там.
Отдельно проверьте сам WP-Cron: на сайтах с низким трафиком он просто не запускается, потому что запускается визитами. Если в wp-config.php стоит DISABLE_WP_CRON, а системный планировщик никто не настроил, отложенные письма не уйдут никогда.
Нет обратной связи от почтовиков
Отправка по SMTP знает только одно: сервер принял письмо. Что было дальше — отбили, положили в спам, адрес не существует — не знает никто. Если письма несут деньги, нужен сервис, который отдаёт bounce-статусы, и живой ящик для rua= из DMARC-записи.
Письма уходят с тестового стенда
Копия базы на staging, кто-то нажал «пересчитать заказы» — и покупатели получают дубликаты писем о заказах трёхмесячной давности. На стенде отправку отключают принудительно, а не надеются на внимательность: либо плагин вроде WP Mail Logging в режиме перехвата, либо тот же pre_wp_mail, который возвращает true и ничего не отправляет.
* Транзакционное письмо — письмо в ответ на действие конкретного человека: регистрация, сброс пароля, подтверждение заказа. В отличие от рассылки, оно идёт одному адресату, не требует отписки и к нему другие требования по скорости доставки.
Что делать дальше
Порядок действий на живом сайте:
- Поставьте mu-плагин с
wp_mail_failedи нормальным отправителем. Теперь у отказов есть причина в логе. - Отправьте тестовое письмо через mail-tester.com и запишите оценку до изменений.
- Закройте DNS: SPF, DKIM, DMARC с
p=noneдля начала. Через пару недель посмотрите отчёты и решайте, ужесточать ли политику. - Выберите подход из таблицы по объёму писем, настройте, повторите тест.
- Включите хранение отправленных писем хотя бы на месяц. Когда клиент скажет «мне ничего не приходило», вы ответите фактом, а не предположением.
Дальше смотрите на два показателя: долю писем, по которым пришёл отказ, и задержку между действием пользователя и доставкой. Первый меряется в панели почтового сервиса, второй — по времени в логе отправки против времени в заголовках полученного письма. Если доля отказов поползла вверх без изменений на сайте, проверяйте не код, а репутацию домена.
Мотивационное письмо и эссе: Поступление, стипендии, гранты, обмен
Спросить за 25 ₽Источники: wp_mail() — WordPress Developer Resources, Рекомендации Google для отправителей писем
Частые вопросы
wp_mail() вернула true, но письма нет. Где искать?
У меня WordPress 5.9, можно пользоваться pre_wp_mail?
Плагин SMTP настроен, тестовое письмо проходит, а письма WooCommerce не идут. Почему?
Хостер блокирует порт 25, что делать?
Можно ли хранить пароль SMTP в настройках плагина?
Что чаще всего ломается в проде после перехода на внешний сервис?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.