Play
online · 30 мин
Туториалы 22 мин чтения 11.09.2026

Не отправляются письма на Битрикс: диагностика почты, SMTP и cron

Письма с сайта на Битрикс перестали уходить? Не спешите винить SMTP: в половине случаев проблема кроется в шаблонах событий или зависших агентах. Разбираем три сценария диагностики, которые сэкономят вам часы поисков.

Не отправляется почта Битрикс: с чего начать диагностику

Когда к нам приходят с формулировкой «письма перестали уходить», первое, что мы делаем — не лезем в настройки SMTP. Потому что «не отправляется почта» в Битриксе — это как минимум три разных диагноза, и лечатся они в разных местах. В нашей практике примерно в половине обращений проблема вообще не в транспорте: письмо даже не дошло до попытки отправки.

Первый сценарий — событие не создалось. Код вызвал Event::send или CEvent::Send, но для этого типа события нет ни одного активного шаблона, либо шаблон привязан к другому сайту (LID), либо не подходит по условиям. В таблицу b_event в этом случае ничего не попадёт — и искать проблему в SMTP бессмысленно.

Второй сценарий — событие создалось, но агент его не обработал. Запись в b_event есть, статус SUCCESS_EXEC = N, а письмо висит в очереди. Классика: агенты не выполняются на хитах, отключены через NO_AGENT_CHECK, или сайт под нагрузкой просто не дотягивается до почтового агента. Об этом подробно ниже — в разборе b_event и агентов.

Третий сценарий — агент отработал, но SMTP отбил письмо. Тогда смотрим лог msmtp в BitrixVM и настройки релея. Здесь уже имеет смысл проверять порт, TLS, авторизацию и то, не улетает ли письмо в спам у получателя.

Мы всегда идём сверху вниз — от события к получателю. Так быстрее отсекаются ложные версии.

  • Проверить b_event: есть ли запись о событии и какой у неё SUCCESS_EXEC.
  • Проверить, выполняются ли агенты вообще (через cron или на хитах).
  • Посмотреть лог msmtp: /home/bitrix/msmtp_default.log в BitrixVM.
  • Проверить спам-фильтр получателя и репутацию домена отправителя.

Начинаем с запроса к b_event — он сразу показывает, на каком из трёх этапов застряло письмо:

SQL
SELECT ID,
       EVENT_NAME,
       DATE_INSERT,
       LID,
       SUCCESS_EXEC,
       DATE_EXEC
FROM b_event
ORDER BY ID DESC
LIMIT 20;

Читать результат просто, если понимать, что означают поля. SUCCESS_EXEC — ключевой индикатор результата отправки уже обработанного события: Y — все письма успешно отправлены, F — ошибка отправки, P — часть писем отправлена, 0 — не найден шаблон. Необработанные события определяются по пустому или нулевому SUCCESS_EXEC и наличию записи в очереди. Если у записи пустой SUCCESS_EXEC и DATE_EXEC пустой — событие зависло, и проблема в агентах, а не в SMTP.

Поле DATE_INSERT показывает, когда событие было создано. Если разница между DATE_INSERT и текущим временем растёт — очередь не разгребается, агент не запускается. LID — привязка к сайту: если событие создалось с LID, для которого нет активного шаблона, письма не будет даже при живом SMTP. DATE_EXEC заполняется в момент фактической обработки — по нему видно, работает ли агент вообще.

В проектах с несколькими сайтами на одной установке мы первым делом сверяем LID у зависших событий с настройками шаблонов — несовпадение встречается чаще, чем кажется. Если SUCCESS_EXEC = Y, а письмо не дошло — переходим к логу msmtp и настройкам релея. Об этом ниже.

Почему Битрикс письма не уходят: разбор b_event и агентов

Самый частый запрос на эту тему звучит так: «письма вроде бы отправляются, ошибок нет, а на ящик ничего не приходит». И почти всегда причина в архитектуре отложенной отправки, которую Битрикс использует по умолчанию. Разберём её по шагам, потому что без понимания этой цепочки любая диагностика превращается в гадание.

Когда вы вызываете Event::send (или его обёртку CEvent::Send), письмо не уходит сразу. Метод лишь создаёт запись в таблице b_event — это очередь. Реальную отправку выполняет почтовый агент: раз в N секунд он выбирает из очереди пачку событий, подставляет шаблоны, формирует тело письма и вызывает mail() или SMTP-транспорт. Если агент не запускается, письма навсегда остаются лежать в b_event.

Ломается цепочка в одном из четырёх мест:

  • создание события — нет активного шаблона для этого типа, и Event::send молча возвращает объект с ошибкой;
  • очередь — запись есть, но агент не срабатывает по расписанию;
  • агент — отключён константой, снят с cron или упал с фаталом;
  • транспорт - mail() недоступен, SMTP-хост не отвечает, письмо режется спам-фильтром.

Дальше — как это выглядит на схеме и что проверять в каждом узле.

flowchart LR
    A[Event::send] -->|"пишет в очередь"| B[b_event]
    B -->|"агент раз в N сек"| C[Почтовый агент]
    C -->|"mail() / SMTP"| D[Транспорт]
    D -->|"доставка"| E[Получатель]
    C -.->|"нет шаблона"| F[Ошибка в результате]
    B -.->|"агент выключен"| G[Очередь копится]

Проверяем, что агенты вообще выполняются

Прежде чем лезть в настройки SMTP, убедитесь, что почтовый агент жив. Самый быстрый способ — запустить его вручную и посмотреть на реакцию. Если после ручного запуска письма начинают уходить пачкой, значит транспорт настроен, а проблема именно в расписании агентов.

PHP
<?php
// Запускаем почтовый агент вручную из CLI или через браузер (только для отладки!)
// В CLI: php /home/bitrix/www/bitrix/modules/main/tools/cron_events.php

require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");

use Bitrix\Main\Loader;
Loader::includeModule("main");

// 1. Смотрим, зарегистрирован ли почтовый агент и когда он последний раз отрабатывал
$res = CAgent::GetList(
    ["LAST_EXEC" => "DESC"],
    ["MODULE_ID" => "main", "%NAME" => "CEvent::CheckEvents"]
);
while ($agent = $res->Fetch()) {
    echo "Агент: " . $agent["NAME"] . "\n";
    echo "  Активен: " . ($agent["ACTIVE"] === "Y" ? "да" : "НЕТ") . "\n";
    echo "  Последний запуск: " . $agent["LAST_EXEC"] . "\n";
    echo "  Интервал: " . $agent["AGENT_INTERVAL"] . " сек\n";
    echo "  Следующий запуск: " . $agent["NEXT_EXEC"] . "\n\n";
}

// 2. Сколько писем висит в очереди прямо сейчас
$count = \Bitrix\Main\Application::getConnection()
->queryScalar("SELECT COUNT(*) FROM b_event WHERE SUCCESS_EXEC = 'N'");
echo "В очереди b_event: " . (int)$count . " писем\n";

Если LAST_EXEC у почтового агента старше нескольких минут — расписание сломано. Если агент активен, но NEXT_EXEC в прошлом — вероятно, не запускается cron или хит-агент выключен.

Что делать, если NO_AGENT_CHECK или DisableEventsCheck включены

Дальше смотрим в dbconn.php и .settings.php. Там часто лежат константы, которые разработчики когда-то добавили «для скорости», а потом забыли.

NO_AGENT_CHECK отключает выполнение агентов на хитах, то есть на обычных запросах к сайту. Логика такая: чтобы агент сработал, Битрикс при каждом заходе пользователя проверяет расписание и, если пора, запускает нужные агенты. С этой константой проверка пропускается — значит, почтовый агент не отработает ни на одном посещении. Письма при этом исправно копятся в b_event, но никто их не разбирает.

DisableEventsCheck действует иначе: он запрещает именно проверку почтовых событий на хитах. То есть даже если агенты в целом работают, конкретно почта не отправляется. Эти константы обычно прописывают в /bitrix/php_interface/dbconn.php или в .settings.php — там же, где BX_CRONTAB_SUPPORT.

Когда их выключение оправдано? Только если вы осознанно перенесли почту на cron через BX_CRONTAB_SUPPORT и не хотите, чтобы хит-агенты дублировали работу. Во всех остальных случаях эти константы — прямая причина «письма не уходят». Проверьте их наличие и закомментируйте, если cron не настроен.

Очередь b_event растёт, но её никто не разбирает: таблица пухнет, письма копятся и уходят одной пачкой после ручного запуска агента. Если такое состояние держится неделями, вы рискуете не только забить БД, но и получить массовую рассылку на устаревшие адреса — а это уже репутация домена и риск попасть в спам-листы.

Настройка почты Битрикс: где живут параметры отправки

Распространённая ошибка: искать «одну кнопку», которая отвечает за отправку писем. В Битриксе её нет — параметры разбросаны по трём уровням, и каждый решает свою задачу. Главный модуль задаёт адрес отправителя, кодировку и метод отправки — то, что видит получатель в поле «От кого». Модуль «Почта» отвечает за приём входящих: ящики POP3/IMAP и правила обработки. А на уровне сервера работает системный MTA или msmtp в BitrixVM — он физически доставляет письмо наружу.

Когда к нам приходит задача «письма не уходят» или «нужно перевести отправку на внешний SMTP», мы первым делом разводим эти три уровня по полочкам. Симптом «письмо не пришло» может родиться на любом из них, а правки в одном месте ничего не дадут, если проблема в другом. Понимание, что где лежит, экономит часы диагностики: вместо перебора всех настроек подряд сразу видно, куда смотреть — в шаблон события, в конфиг сервера или в параметры модуля.

Ниже — матрица, по которой удобно ориентироваться.

Уровень настройки Где правится Что задаёт Типичная ошибка
Главный модуль Адрес отправителя, кодировка, метод отправки From не совпадает с доменом сайта
Модуль «Почта» Настройки → Настройки продукта → Настройки модулей → Почта Ящики POP3/IMAP, правила обработки входящих Правила конфликтуют с шаблонами событий
Сервер / BitrixVM Меню BitrixVM: Configure pool sites → Change e-mail settings Хост, порт, авторизация, доставка через msmtp Не задан from address на уровне сервера

Настройки главного модуля: адрес отправителя и метод отправки

Это первый уровень, с которого стоит начинать. Именно здесь задаётся адрес отправителя — тот, что попадёт в заголовок From каждого письма. И здесь же выбирается метод отправки: через стандартную функцию mail() или через внешний SMTP-релей.

Ключевое правило: отправлять письма от имени домена имеют право только те серверы, которые перечислены в SPF-записи этого домена. Если сайт живёт на shop.example.ru, а письма уходят через сторонний сервис, не указанный в SPF-записи домена, почтовые провайдеры получателя сверяют IP-адрес отправителя со списком в SPF и почти гарантированно отправляют такое письмо в спам или вовсе отклоняют. Мы регулярно видим это у магазинов, где отправку когда-то настроили «на глазок» и забыли.

Чтобы письма доходили, домен должен подтверждать право отправки через SPF и DKIM. SPF перечисляет серверы, которым разрешено слать почту от имени домена, DKIM подписывает письмо криптографическим ключом. Если отправитель и домен совпадают, а записи настроены — письмо проходит проверки. Если нет — даже рабочий SMTP не спасёт.

Модуль «Почта»: когда он нужен и когда мешает

Модуль «Почта» часто путают с настройками исходящей отправки — и это вторая по частоте ошибка после адреса отправителя. модуль включает настройки ящиков POP3/IMAP и правила обработки входящих. Для исходящих писем он не нужен вообще: их обрабатывает главный модуль и агенты.

Модуль имеет смысл, когда нужно забирать почту с внешнего ящика в Битрикс — например, превращать входящие письма в тикеты или лиды. Тогда вы настраиваете подключение к ящику и правила: по какому адресу, с какой темой, в какой сущности создавать запись.

Грабли в другом: правила обработки входящих могут конфликтовать с шаблонами почтовых событий. Если правило настроено слишком широко, оно перехватывает письма, которые должны были уйти по шаблону события, и вместо отправки получатель видит «письмо обработано правилом».

В проектах, где модуль «Почта» включён «на всякий случай», мы советуем сначала проверить, не он ли глушит исходящие, и только потом лезть в SMTP. Общая логика простая: главный модуль — за то, что и от кого уходит; модуль «Почта» — за то, что и как приходит; сервер — за то, доставится ли письмо наружу. Разводите эти зоны — и диагностика перестаёт быть гаданием.

SMTP Битрикс: как подключить внешний релей и не сломать отправку

Типичный сценарий: магазин на Битриксе отправляет письма через локальный mail(), но заказы и уведомления всё чаще не доходят — падают в спам или молча отбиваются на стороне провайдера. Письмо с IP хостинга без репутации и без корректно настроенного домена почти гарантированно получает отказ или метку «подозрительное». Заметнее всего это на проектах с активным потоком транзакционных писем: подтверждения заказа, регистрация, восстановление пароля, уведомления о доставке. Чем больше писем уходит с одного сервера, тем быстрее провайдеры начинают фильтровать такой трафик.

Решение — подключить внешний SMTP-релей: Яндекс.Почта для домена, Mail.ru, собственный корпоративный сервер или транзакционный сервис. Релей берёт на себя репутацию, доставку и обработку отказов, а Битрикс просто передаёт ему письма по авторизованному каналу.

У нас это стандартный шаг при переносе сайта на прод или при жалобах клиентов на «письма не приходят». Ниже — два рабочих способа настройки: через .settings.php и через msmtp в BitrixVM. И один подводный камень, из-за которого SMTP «вроде настроен, но не работает».

Вариант 1: SMTP через .settings.php

В современном Битриксе настройку SMTP можно выполнить через административную панель: по данным официальной документации, страница настроек SMTP-подключений доступна в разделе «Настройки» → «Настройки продукта» → «Почтовые и СМС события» → «Настройки SMTP», где можно добавлять, просматривать и изменять SMTP-подключения. Также в BitrixVM настройка почты выполняется через меню: 6. Configure pool sites → 4. Change e-mail settings on site, где требуется указать имя хоста и адрес отправителя; для отправки используется пакет msmtp. Правки в dbconn.php для этого не нужны.

PHP
<?php
// /bitrix/.settings.php — фрагмент, добавляем ключ 'mail' в общий массив
return [
    // ... остальные секции (crypto, cache, exception_handling и т.д.)
    'mail' => [
        'value' => [
            'enabled' => true,
            'host'    => 'smtp.yandex.ru',
            'port'    => 465,
            'login'   => 'robot@your-domain.ru',
            'password' => 'app-password-here', // пароль приложения, не от ящика
            'tls'     => true,
            'from'    => 'robot@your-domain.ru',
        ],
        'readonly' => false,
    ],
];

Ключевое здесь — секция mail с полем enabled: по официальной документации параметр enabled включает возможность использования SMTP-сервера отправителя. Поле tls включает шифрование — по практике для внешних SMTP-сервисов (например, Яндекс) обычно указывают порт 465 или 587 и включают tls, однако конкретные требования зависят от провайдера. Адрес в from должен совпадать с логином или быть его алиасом, иначе релей вернёт ошибку авторизации отправителя.

Этот способ предпочтительнее правки dbconn.php, потому что .settings.php — штатный механизм конфигурации в D7, он не перезаписывается обновлениями и не конфликтует с миграциями. Если проект разворачивается через CI, секцию mail удобно подставлять из переменных окружения на этапе деплоя.

На старых сборках, где секции mail в .settings.php ещё нет, есть два пути: обновить ядро до версии, где она поддерживается, либо использовать msmtp (о нём ниже) или функцию bxmail как обёртку. В сообществе также встречается вариант с дополнительными константами в dbconn.php — но это не подтверждено официальной документацией, поэтому применяем его с осторожностью и только когда обновление ядра невозможно.

Вариант 2: msmtp в BitrixVM

Если сайт крутится на BitrixVM, отдельный SMTP-конфиг в Битриксе не нужен — окружение уже шлёт почту через msmtp. Настраивается он из меню виртуальной машины:

  1. Открыть меню BitrixVM и выбрать 6. Configure pool sites.
  2. Выбрать пункт 4. Change e-mail settings on site.
  3. Ввести имя SMTP-хоста (например, smtp.yandex.ru) и адрес отправителя (from address).
  4. После сохранения проверить результат в логе /home/bitrix/msmtp_default.log — там видно каждую попытку соединения и ответ релея.
SHELL
# Тестовое письмо через msmtp из консоли VM
echo -e "Subject: test\nFrom: robot@your-domain.ru\n\nSMTP test" | msmtp -a default you@example.com

# Смотрим, что ответил релей
tail -n 50 /home/bitrix/msmtp_default.log

Лог — главный инструмент диагностики в этом варианте. Если письмо не ушло, в msmtp_default.log будет видна причина: неверный пароль, отказ по TLS, блокировка по IP или неверный from. Без просмотра лога настройка превращается в угадывание.

Порты 465 и 587: какой выбрать

Здесь есть противоречие, с которым сталкивается каждый, кто настраивает Яндекс.Почту: разные источники называют рабочими и 465, и 587. Оба действительно работают, но по-разному устанавливают соединение. Путаница между ними — частая причина «SMTP настроен, а письма не уходят».

Порт 465 — это implicit TLS: шифрование включается сразу при открытии сокета, ещё до приветствия SMTP. Порт 587 — STARTTLS: сначала идёт обычное соединение, потом клиент командой STARTTLS повышает его до шифрованного. Если указать 587, а в конфиге выставить tls => true в режиме implicit, соединение повиснет или отвалится по таймауту.

По нашему опыту с Яндексом и Mail.ru надёжнее всего порт 465 с явным TLS — он меньше зависит от того, как клиент договаривается о шифровании. Но если релей требует именно STARTTLS (так делает часть корпоративных серверов), берите 587. Проверяется это логом: при 465 в msmtp_default.log или в отладке Битрикса вы увидите успешное TLS negotiation сразу, при 587 — сначала обычное 220 smtp..., затем STARTTLS и уже потом авторизацию. Если в логе после STARTTLS идёт разрыв — порт выбран неверно для вашей конфигурации шифрования.

Пароль приложения, а не пароль от ящика. Яндекс и Mail.ru не принимают обычный пароль учётной записи при SMTP-авторизации — соединение молча отбивается, а в логе появляется невнятное authentication failed. Нужно зайти в настройки безопасности почтового ящика, включить двухфакторную аутентификацию и сгенерировать пароль приложения. Именно его указывайте в .settings.php или в меню BitrixVM. Если после смены пароля письма всё равно не идут, первым делом проверьте, что не остался старый пароль от ящика.

Как перенести отправку писем на cron через BX_CRONTAB_SUPPORT

Типичный сценарий: сайт на Bitrix с приличным трафиком, магазин принимает заказы, письма вроде бы уходят — но с задержкой в 10–20 минут, а под вечер пятницы и вовсе копятся в очереди. Причина в том, что почтовые события разбирает агент, а агенты в Битриксе привязаны к хитам: пока на сайт заходят, они крутятся; ночью или на просадке трафика стоят. Таблица b_event растёт, клиент ждёт подтверждение заказа.

Решение — перевести почту на регулярный cron. В Битриксе за это отвечает константа BX_CRONTAB_SUPPORT: она сообщает системе, что все агенты и почтовые события запускаются не на хитах, а по расписанию системного планировщика. У нас в проектах с активной отправкой (заказы, чеки, уведомления о сбросе пароля) это стандартный шаг при настройке почты, наравне с подключением SMTP. Ниже — куда прописывать константу, что делать со сторонними модулями и как настроить сам cron.

Куда прописывать константу и почему именно dbconn.php

Константу размещают в файле /bitrix/php_interface/dbconn.php — он подключается до инициализации модулей и до того, как система начинает работать с агентами. Если прописать BX_CRONTAB_SUPPORT в init.php или в .settings.php, часть проверок к моменту выполнения уже пройдёт, и почта продолжит обрабатываться на хитах.

PHP
<?php
// /bitrix/php_interface/dbconn.php

// Переносим выполнение почтовых событий и агентов на cron
define("BX_CRONTAB_SUPPORT", true);

// Опционально: отключаем проверку агентов на хитах совсем
// (полезно, если cron настроен и работает стабильно)
// define("NO_AGENT_CHECK", true);

Файл dbconn.php уже существует в любом проекте — просто дописываем строку в конец. Никаких переопределений в других файлах делать не нужно.

Что делать, если стоит сторонний модуль «Агенты на кроне»

Здесь есть известное расхождение. Официальная документация Битрикса говорит однозначно: чтобы почтовые события ушли на cron, константу BX_CRONTAB_SUPPORT нужно прописать в dbconn.php. А в сообществе, например на форумах разработчиков, встречается мнение, что при установленном стороннем модуле «Агенты на кроне» константа необязательна: модуль сам перехватывает запуск агентов и переносит их на планировщик.

Наша позиция — прописываем всегда. Причина простая: модуль «Агенты на кроне» управляет агентами, но почтовые события в b_event обрабатываются отдельным механизмом, и он тоже завязан на BX_CRONTAB_SUPPORT. То есть даже если агенты у вас уже под cron, без константы почта может продолжать разбираться на хитах — или, что хуже, не разбираться вообще, если агент почты отключён. Мы регулярно видим проекты, где модуль стоит, константы нет, и письма «куда-то пропадают». Одна строка в dbconn.php снимает этот вопрос полностью.

Если сторонний модуль уже настроен и ломать его не хочется — просто добавьте константу рядом. Конфликта не будет: BX_CRONTAB_SUPPORT лишь сообщает системе, что агенты запускаются извне, а кто именно их запускает, модуль или системный cron, уже неважно.

  • Настроить системный cron — добавить задание на запуск bitrix/modules/main/tools/cron_events.php от пользователя сайта.
  • Выбрать интервал — для почты обычно достаточно раз в минуту; реже — письма начнут копиться, чаще — смысла нет.
  • Проверить через лог — запустить скрипт вручную и убедиться, что b_event разбирается, а не остаётся с флагом SUCCESS_EXEC = N.
  • Закрыть cron от веба — файл cron_events.php не должен быть доступен по HTTP, иначе его дёрнет любой бот.

Event::send против CEvent::Send: какой метод выбрать и как передать вложение

Многие думают, что CEvent::Send и Event::send — это два разных механизма отправки. С точки зрения доставки разницы нет: CEvent::Send — тонкая обёртка над D7-методом, который делает всю работу. Разница вылезает в двух местах: при передаче вложений и при требовании немедленной доставки. Именно тут ломаются проекты, когда магазин шлёт счёт с PDF, а письмо уходит без файла — или уходит через час после заказа.

У нас в практике переход на D7-вызовы почти всегда сопровождается одной и той же ошибкой. Разработчик берёт старый сниппет с CEvent::Send, копирует его в новый модуль, добавляет массив путей к файлам — и получает письмо с вложением-хэшем в имени. Работает, но клиент открывает ящик и видит a3f9c2b1d4.pdf вместо Счёт_№1247.pdf. Ниже разберём, как этого избежать и когда вообще стоит трогать SendImmediate.

Метод Отложенность Вложения Duplicate Когда использовать
Event::send Через b_event и агент ID из b_file или пути Нет Стандартные уведомления
CEvent::Send Через b_event и агент Пути, сохраняются в b_file Нет Legacy-код
CEvent::SendImmediate Сразу, минуя b_event Пути, сохраняются в b_file Есть (15.0.7+) Коды подтверждения, TTL-письма

Передача вложения: ID из b_file или абсолютный путь

D7-метод принимает в поле вложений либо массив ID из таблицы b_file, либо массив абсолютных путей к файлам на диске. Первый вариант предпочтительнее: файл уже лежит в хранилище Битрикса, дублирования не будет, а имя сохраняется так, как его записали при загрузке. Второй удобен, когда файл генерируется на лету — например, PDF-счёт из шаблона.

PHP
use Bitrix\Main\Mail\Event;

// Вариант 1: файлы уже в b_file — передаём ID
$fileIds = [
    (int)$invoiceFileId, // из CFile::SaveFile() или из поля UF_*
    (int)$actFileId,
];

Event::send([
        'EVENT_NAME' => 'ORDER_INVOICE',
        'LID'        => SITE_ID,
        'C_FIELDS'   => [
            'ORDER_ID' => $orderId,
            'EMAIL'    => $email,
        ],
        'FILE'       => $fileIds,
]);

// Вариант 2: генерируем PDF на лету — передаём абсолютные пути
$tmpPath = $_SERVER['DOCUMENT_ROOT'] . '/upload/tmp/invoice_' . $orderId . '.pdf';

Event::send([
        'EVENT_NAME' => 'ORDER_INVOICE',
        'LID'        => SITE_ID,
        'C_FIELDS'   => [
            'ORDER_ID' => $orderId,
            'EMAIL'    => $email,
        ],
        'FILE'       => [$tmpPath],
]);

Что здесь ключевое. В первом варианте имя файла в письме будет ровно таким, каким оно сохранено в b_file. Во втором система скопирует файл, создаст запись в b_event и приложит его к письму. Менять под себя вы будете в первую очередь способ получения $invoiceFileId. У кого-то это пользовательское поле заказа, у кого-то прямая выборка из b_file по ID, у кого-то результат CFile::SaveFile() после генерации. Вложений несколько? Просто добавляйте элементы в массив, порядок сохраняется.

Если файл генерируется в рантайме и после отправки не нужен, второй вариант удобнее: не надо чистить b_file. Но помните: при передаче пути система всё равно создаст запись в b_file через CFile::SaveFile, а b_event получит ссылку на неё. «Временных» файлов в базе не бывает — за ними придётся следить отдельно, если объём генерации большой.

Когда нужен SendImmediate и параметр Duplicate

SendImmediate отправляет письмо прямо в момент вызова, минуя таблицу b_event и агент. Спасает в сценариях, где задержка критична: код подтверждения регистрации, одноразовая ссылка на сброс пароля, уведомление о попытке входа. Письмо с коротким TTL не должно ждать следующего прогона агента, иначе пользователь получит код, когда он уже истёк. Побочный эффект: если SMTP-сервер недоступен, ошибку вы получите синхронно, в том же запросе, а не «когда-нибудь потом».

С версии 15.0.7 у SendImmediate появился параметр Duplicate — он отправляет копию письма на адрес, указанный в настройках главного модуля. Удобно для отладки шаблонов на продакшене и для внутреннего контроля: клиенту уходит рабочее письмо, вам его копия. Включайте осознанно — на активном сайте это удвоит поток исходящих писем и расход квоты SMTP-провайдера.

Грабли: если для кода события нет ни одного активного шаблона, Event::send не выбросит исключение — он вернёт объект результата с ошибками внутри. Разработчики часто вызывают метод и не проверяют результат, а потом удивляются, почему письма «не уходят». Всегда читайте $result->getErrorMessages() — там будет явное «шаблон не найден» или «получатель не указан». Без этой проверки вы будете искать проблему в SMTP, хотя она в шаблоне.

Письма уходят в спам: что проверить на стороне домена и получателя

Бывает так: SMTP настроен, в логах отправки чисто, а письма всё равно не доходят до инбокса — падают в «Спам» или «Промоакции». И это не проблема Битрикса. Когда к нам приходят с жалобой «письма уходят, но их никто не видит», мы первым делом смотрим не в код, а на внешние факторы доставки: репутацию IP-адреса релея, DNS-записи домена, совпадение отправителя и содержимое письма. Внутренняя логика отправки к этому моменту уже отработала. Дальше письмо живёт по правилам почтовых провайдеров, и именно там решается его судьба.

Эти факторы комбинируются: даже идеально настроенный SPF не спасёт, если IP релея в блок-листе, а тема письма пестрит словами-триггерами. Поэтому проверять нужно весь путь письма — от домена отправителя до тестового ящика получателя.

Чек-лист, с которого мы начинаем разбор:

  • SPF и DKIM настроены для домена отправителя — без них крупные провайдеры режут письма сразу
  • From-адрес совпадает с доменом, для которого настроены записи — не noreply@mail.ru при отправке от имени магазина
  • В письме нет ссылок на подозрительные или чужие домены — редиректы и сокращатели повышают спам-скор
  • В теме и теле письма нет слов-триггеров вроде «акция», «бесплатно», «срочно» в верхнем регистре
  • IP SMTP-релея не в блок-листах — проверяется через публичные сервисы репутации

Почему мы не даём в статье конкретные значения SPF и DKIM? Потому что они зависят от провайдера и домена. Запись SPF для Яндекс.Почты, Mail.ru и корпоративного Google Workspace отличается набором include-директив. DKIM-ключ вообще уникален для каждого домена — его генерирует провайдер при подключении, и подставить «универсальное значение» невозможно.

Поэтому мы показываем принцип. SPF говорит получателю, какие серверы имеют право отправлять письма от вашего домена. DKIM подписывает письмо приватным ключом, а получатель проверяет подпись публичным. DMARC связывает их вместе и говорит, что делать с письмами без валидной подписи. Конкретные строки для DNS-панели берите в документации вашего почтового провайдера — там же обычно есть готовый генератор записей и инструкция по добавлению в панель управления доменом.

Самые частые ошибки, которые ломают отправку почты на Битрикс

Проводя аудит чужих проектов, мы почти всегда видим один и тот же набор грабель. Из-за них почта уходит в никуда или отваливается через месяц после сдачи. Ошибки типовые: от пустого поля «От кого» в настройках до правок в dbconn.php без бэкапа. Мы собрали их в таблицу: слева — что именно ломает отправку, в центре — почему это происходит, справа — как починить. Держите её под рукой, когда разбираете очередной «письма не приходят».

Грабля Причина Решение
Пустой from Не заполнено поле «От кого» в настройках главного модуля Задать адрес отправителя на домене сайта, совпадающий с SMTP-логином
Отключённые агенты Агенты не запускаются на хитах или сняты вручную Проверить агенты почты и перенести их на cron
Пароль от ящика вместо app-password Провайдер требует пароль приложения для SMTP Сгенерировать app-password в панели почтового сервиса
Порт 25 у провайдера Хостер режет исходящий 25-й порт Перейти на 465 или 587 с TLS
Шаблон без активной версии У почтового шаблона нет активной версии или он отключён Включить шаблон и проверить привязку к типу события
NO_AGENT_CHECK в dbconn.php Константа отключает проверку агентов на хитах Убрать или перенести отправку на cron осознанно

Чтобы эти ошибки не возвращались, у нас в студии выстроен простой процесс. Перед релизом проверяем заполненный from, активные шаблоны, рабочий SMTP и тестовое письмо на внешний ящик. Дальше — мониторинг размера b_event: если таблица растёт и не чистится, значит агент отправки не отрабатывает. Видно задолго до жалоб клиента.

Ещё скрипт раз в час считает неотправленные записи и пишет в чат, если их больше нормы.

Конфигурацию фиксируем в репозитории: константы из dbconn.php и настройки SMTP — в документации проекта, чтобы следующий разработчик не гадал, почему почта работает именно так. Такой набор правил внедряется один раз, и он снимает большую часть обращений «письма опять не уходят».

Подробнее об услуге: поддержка и сопровождение сайтов на Битрикс.

Частые вопросы

Можно ли отправлять письма через SMTP с авторизацией по OAuth (Gmail, Яндекс 360), а не по логину/паролю?

Да, но из коробки Bitrix поддерживает только LOGIN и PLAIN. Для OAuth нужен внешний релей (msmtp, postfix с sasl) или кастомный обработчик через событие OnBeforeEventSend с отправкой через PHPMailer с XOAUTH2.

Что делать, если b_event заполняется, а письма не приходят и в msmtp логе пусто?

Скорее всего, агент отправки не отрабатывает: проверьте, что агенты запускаются (bitrix/tools/agents.php или cron), и что в настройках модуля main не включён режим «отправлять через cron» без настроенного BX_CRONTAB_SUPPORT. Также убедитесь, что в b_event поле SUCCESS_EXEC = 'N' и DATE_EXEC пустой.

Чем отличается Event::send от CEvent::Send и что использовать в новом коде?

Event::send — это D7-обёртка (\Bitrix\Main\Mail\Event), CEvent::Send — старый API. В новом коде используйте Event::send, он поддерживает вложения через addAttachment и корректно работает с шаблонами; CEvent::Send оставляйте только для legacy-кода.

А если у меня BitrixVM и письма уходят, но только при заходе в админку, а по cron — нет?

Проверьте, что cron запускается от пользователя bitrix и что в /etc/cron.d/bitrix_agents указан правильный путь к php. Часто проблема в том, что msmtp настроен только для shell-пользователя bitrix, а cron работает с другим HOME и не видит ~/.msmtprc.

Можно ли отправлять вложения через Event::send и как передать файл из b_file?

Да: получите путь через CFile::GetPath($fileId) или CFile::MakeFileArray($fileId), затем вызовите $event->addAttachment($path). В шаблоне письма вложение подхватится автоматически, отдельно прописывать #ATTACHMENT# не нужно.

Что делать, если письма уходят, но попадают в спам у Gmail, хотя SPF и DKIM настроены?

Проверьте DMARC (должен быть хотя бы p=none с rua), PTR-запись для IP отправителя и что From-домен совпадает с доменом, подписанным DKIM. Также уберите в шаблоне ссылки на bitrix-адреса вида /bitrix/admin и не ставьте в теме письма CAPS и слово «тест».

#Bitrix #PHP #Cron
автор · Backend / SRE Engineer
Артем Колячек

Backend-разработчик и SRE в студии Paradigma. Занимается тем, что у других проектов обычно обнаруживается за неделю до запуска: переездом Bitrix-проектов между серверами, починкой кешей после миграций, выстраиванием pipeline для контейнеров, мониторингом под нагрузкой.

До Paradigma — 7 лет в backend (PHP + Postgres) и в operations (Linux, Docker, Coolify, restic-бэкапы). Любит когда логи разговаривают полным синтаксисом ошибки, а не «что-то пошло не так».

На блоге пишет ровно про те ситуации с которыми сам разбирался руками: какой Bitrix-апдейт сломал кеш и как откатить, почему Docker-сеть не находит контейнер после рекрейта, что делать когда asyncpg ругается на event loop.