Отправка письма из PHP: частые ошибки новичков
Письма из PHP уходят «в никуда» или падают в спам, а функция mail() возвращает true. Разбираем, что нужно настроить заранее и почему отправлять письма без SMTP-библиотеки — плохая идея.

В конце этой инструкции у вас будет рабочая отправка письма из PHP через SMTP-сервер, которое дойдёт до ящика, а не потеряется по дороге. На настройку уйдёт 20-30 минут, если у вас уже есть почтовый аккаунт и доступ к консоли или FTP проекта. Мы не будем разбирать встроенную функцию mail() как основной способ — покажем, почему она подводит, и сразу перейдём к библиотеке PHPMailer, которая закрывает большинство новичковых граблей.
Что нужно иметь до начала
Соберите это заранее, иначе застрянете на середине:
- PHP 8.1 или новее с расширениями
opensslиmbstring(обычно включены по умолчанию). Проверить: командаphp -mдолжна показать обе строки. - Composer — менеджер пакетов PHP1. Без него можно, но с ним ставить PHPMailer в разы проще.
- Доступ к SMTP-серверу. Это отдельный почтовый сервис: ваш хостинг, Яндекс 360, Mail.ru для бизнеса или транзакционный сервис вроде провайдера рассылок. Вам нужны хост, порт, логин и пароль (или пароль приложения).
Если вы планировали просто вызвать mail() и не поднимать SMTP — сначала прочитайте раздел ниже, почему это заканчивается письмами в спаме.
Почему mail() возвращает true, а письма нет
Главная ловушка: mail() возвращает true, если письмо принято локальным почтовым агентом (обычно sendmail) на отправку. Это не значит, что оно ушло с сервера и тем более дошло до адресата. Дальше письмо может отсеяться по трём причинам:
- у сервера нет корректных DNS-записей SPF и DKIM, поэтому Gmail и Яндекс кладут письмо в спам или отбивают;
- на shared-хостинге функция
mail()часто отключена или её IP уже в чёрных списках из-за чужих рассылок; - заголовки собраны вручную с ошибкой в кодировке — тема превращается в «крокозябры».
Если письмо ушло, но его не видно, проблема почти всегда не в PHP, а в почтовой инфраструктуре: SPF, DKIM и репутации IP. Код лишь передаёт письмо, доставку решает почта.
Поэтому боевой путь — не собирать SMTP-диалог руками, а отдавать письмо готовой библиотеке, которая подключается к настроенному SMTP-серверу с авторизацией и шифрованием.
Шаг 1. Установите PHPMailer
В корне проекта выполните:
composer require phpmailer/phpmailer
После установки появится папка vendor/ и файл composer.json с зависимостью. Проверка: в composer.json в секции require должна быть строка вида "phpmailer/phpmailer": "^6.9" (версия 6.x на момент написания).
Если Composer недоступен, скачайте релиз PHPMailer с GitHub и подключите классы вручную через require — но с Composer автозагрузка настраивается сама.
Шаг 2. Напишите скрипт отправки
Создайте файл send.php. Ниже полный рабочий пример — его можно скопировать целиком и подставить свои данные:
<?php
// PHPMailer 6.x, PHP 8.1+
require __DIR__ . '/vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer(true); // true — включает исключения при ошибках
try {
// Настройки SMTP
$mail->isSMTP();
$mail->Host = 'smtp.example.com'; // хост вашего SMTP
$mail->SMTPAuth = true;
$mail->Username = 'noreply@example.com'; // логин на SMTP
$mail->Password = 'YOUR_SMTP_PASSWORD'; // пароль или пароль приложения
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; // шифрование
$mail->Port = 465; // 465 для SMTPS, 587 для STARTTLS
$mail->CharSet = 'UTF-8'; // иначе кириллица ломается
// Кто отправляет и кому
$mail->setFrom('noreply@example.com', 'Мой сайт');
$mail->addAddress('client@example.org', 'Клиент');
$mail->addReplyTo('support@example.com', 'Поддержка');
// Содержимое
$mail->isHTML(true);
$mail->Subject = 'Заявка принята';
$mail->Body = '<p>Спасибо, мы получили вашу заявку.</p>';
$mail->AltBody = 'Спасибо, мы получили вашу заявку.'; // для текстовых клиентов
$mail->send();
echo 'Письмо отправлено';
} catch (Exception $e) {
// ErrorInfo содержит понятное описание проблемы
echo 'Ошибка отправки: ' . $mail->ErrorInfo;
}
Проверка: запустите php send.php в консоли. В случае успеха увидите «Письмо отправлено», а письмо придёт на адрес из addAddress. Проверьте папку «Спам» — на первых отправках оно может попасть туда, пока не настроены DNS-записи.
Обратите внимание: пароль в коде — это заглушка YOUR_SMTP_PASSWORD. В боевом проекте храните его в переменной окружения или в файле конфигурации вне репозитория, а не прямо в скрипте.
Шаг 3. Разберитесь с портами и шифрованием
Самая частая путаница у новичков — порт и тип шифрования не совпадают. Вот рабочие сочетания:
| Порт | Значение SMTPSecure | Когда использовать |
|---|---|---|
| 465 | ENCRYPTION_SMTPS | Соединение шифруется сразу. Работает у большинства провайдеров. |
| 587 | ENCRYPTION_STARTTLS | Шифрование включается после подключения. Рекомендуется многими сервисами. |
| 25 | — | Без авторизации, часто заблокирован хостингами. Для клиентской отправки не используйте. |
Точные хост, порт и способ авторизации возьмите в справке вашего почтового сервиса — у Яндекса, Mail.ru и Gmail они разные, и у многих для внешних программ нужен отдельный «пароль приложения», а не обычный пароль от ящика.
Если не получилось
Типичные ошибки, которые вы увидите в $mail->ErrorInfo, и что с ними делать:
- SMTP connect() failed — сервер недоступен по этому хосту или порту. Проверьте, не блокирует ли хостинг исходящие соединения на 465/587, и не опечатались ли в
Host. - SMTP Error: Could not authenticate — неверный логин или пароль. На Gmail, Яндексе и Mail.ru обычной пары логин-пароль часто недостаточно: включите двухфакторную аутентификацию и создайте пароль приложения.
- Could not instantiate mail function — вы забыли вызвать
isSMTP(), и PHPMailer пытается использовать локальныйmail(), которого нет. Добавьте настройки SMTP. - Кириллица в теме превратилась в «Ð¡Ð¿Ð°Ñибо» — не задан
$mail->CharSet = 'UTF-8'. Установите его до заполнения темы и тела. - Письмо ушло, но попало в спам — это уже не про PHP. Нужны SPF и DKIM для вашего домена. Настраивает их администратор домена или ваш почтовый сервис; без них массовая доставка невозможна.
Что делать дальше
Когда единичная отправка работает, займитесь двумя вещами. Первое — настройте SPF и DKIM для домена отправителя, иначе на большом объёме письма будут уходить в спам; записи добавляются в DNS домена, инструкции даёт ваш почтовый сервис. Второе — не отправляйте письма синхронно прямо в обработчике формы: если SMTP тормозит, пользователь ждёт. Складывайте задачи на отправку в очередь (простейший вариант — таблица в БД плюс cron-скрипт), а рассылку выполняйте отдельным процессом. Массовые рассылки и работа с очередями — отдельная тема, здесь мы разобрали только базовую отправку одного письма.
1 Composer — стандартный менеджер зависимостей для PHP: скачивает библиотеки и настраивает автозагрузку классов, чтобы их не приходилось подключать вручную через require.
Мотивационное письмо и эссе: Поступление, стипендии, гранты, обмен
Спросить за 25 ₽Источники: PHPMailer — официальный репозиторий и документация, PHP: mail — Manual
Частые вопросы
Можно ли обойтись без PHPMailer и отправлять через mail()?
У меня PHP 7.4, пример подойдёт?
Скрипт пишет «Письмо отправлено», но письма нет — что не так?
Где хранить пароль от SMTP, чтобы не светить его в коде?
Почему Gmail и Яндекс не пускают с обычным паролем от почты?
Как отправлять много писем, не блокируя сайт?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.