Отправка письма из PHP: пошаговая инструкция для новичка
Разберём, как отправить письмо из PHP так, чтобы оно дошло, а не упало в спам. От встроенной mail() до SMTP через PHPMailer — с рабочим кодом и разбором частых ошибок.

К концу этой инструкции вы отправите письмо из PHP-скрипта на свою почту через SMTP-сервер и увидите его во «Входящих», а не в спаме. Займёт это 20–30 минут: 5 минут на установку библиотеки, остальное — на получение SMTP-доступов и первую отладку. Разбираем два пути: встроенную функцию mail() (быстро, но письма часто теряются) и библиотеку PHPMailer через SMTP (надёжно, так делают в продакшене).
Что нужно иметь до начала
Прежде чем писать код, убедитесь, что у вас есть:
- PHP 8.1 или новее. Проверьте командой
php -vв терминале. Код ниже написан и проверен на PHP 8.x. На 7.4 он тоже запустится, но 7.4 давно без поддержки — обновитесь. - Composer — менеджер пакетов для PHP. Проверьте:
composer --version. Если команды нет, установите с getcomposer.org. - SMTP-доступы. Это логин, пароль, адрес сервера и порт вашего почтового сервиса. Без них через SMTP письмо не отправить. Где взять — ниже в шаге 2.
Функция mail() работает без Composer и SMTP-доступов, но у неё есть цена — о ней в конце.
Способ 1. Функция mail() — быстро и ненадёжно
Встроенная функция mail() отдаёт письмо локальному почтовому агенту сервера (обычно sendmail на Linux). Она есть в любой сборке PHP, ставить ничего не нужно.
<?php
// PHP 8.x, функция mail() входит в ядро
$to = 'you@example.com';
$subject = 'Тест из PHP';
$body = 'Это первое письмо, отправленное функцией mail().';
// Заголовки: без корректного From письмо почти гарантированно уйдёт в спам
$headers = [
'From' => 'noreply@ваш-домен.ru',
'Content-Type' => 'text/plain; charset=UTF-8',
];
$ok = mail($to, $subject, $body, $headers);
if ($ok) {
echo "Письмо принято к отправке\n";
} else {
echo "mail() вернула false — отправка не удалась\n";
}
Как понять, что сработало. В консоли появится строка «Письмо принято к отправке». Но это не значит, что письмо дошло: mail() возвращает true, если письмо принял локальный агент, а не если его получил адресат. Проверьте почтовый ящик и папку «Спам».
Главная беда способа: у большинства хостингов и локальных машин sendmail либо не настроен, либо отправляет с IP без обратной DNS-записи и без SPF/DKIM — почтовые системы такое режут. Поэтому для реальных проектов переходите к способу 2.
Способ 2. PHPMailer через SMTP — так делают правильно
PHPMailer подключается к настоящему SMTP-серверу вашего почтового сервиса, авторизуется логином и паролем и отправляет письмо от его имени. Такое письмо проходит проверки подлинности и доходит.
Шаг 1. Установите PHPMailer
В папке проекта выполните:
composer require phpmailer/phpmailer
Как понять, что сработало. Появилась папка vendor/ и файлы composer.json, composer.lock. В composer.json в разделе require будет строка вида "phpmailer/phpmailer": "^6.x".
Шаг 2. Получите SMTP-доступы
Данные для подключения выдаёт ваш почтовый провайдер. Не выдумывайте порты — берите из панели или справки сервиса. Типичные значения:
| Параметр | Что вписать | Где взять |
|---|---|---|
| Host (адрес сервера) | напр. smtp.yandex.ru | справка почтового сервиса |
| Port (порт) | 465 (SSL) или 587 (STARTTLS) | там же |
| Username | ваш полный email | ваш логин |
| Password | пароль приложения, не основной | настройки безопасности почты |
Важный момент: для Gmail, Яндекса и большинства крупных сервисов основной пароль от аккаунта в SMTP не подойдёт — нужен отдельный пароль приложения, который создаётся в настройках безопасности почты. Точные адреса серверов и порты сверяйте в справке вашего сервиса, они у всех разные.
Шаг 3. Напишите скрипт отправки
<?php
// PHP 8.x, PHPMailer 6.x
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\SMTP;
require __DIR__ . '/vendor/autoload.php';
$mail = new PHPMailer(true); // true — включает выброс исключений при ошибке
try {
// Настройки SMTP
$mail->isSMTP();
$mail->Host = 'smtp.example.com'; // адрес вашего SMTP
$mail->SMTPAuth = true;
$mail->Username = 'you@example.com'; // логин
$mail->Password = 'YOUR_APP_PASSWORD'; // пароль приложения
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; // для порта 465
$mail->Port = 465;
$mail->CharSet = 'UTF-8';
// Отправитель и получатель
$mail->setFrom('you@example.com', 'Мой сайт');
$mail->addAddress('recipient@example.com', 'Получатель');
// Содержимое
$mail->isHTML(true);
$mail->Subject = 'Тест через PHPMailer';
$mail->Body = '<b>Письмо</b> отправлено через SMTP.';
$mail->AltBody = 'Письмо отправлено через SMTP.'; // для клиентов без HTML
$mail->send();
echo "Письмо отправлено\n";
} catch (Exception $e) {
echo "Ошибка отправки: {$mail->ErrorInfo}\n";
}
Если ваш сервис использует порт 587 вместо 465, замените две строки: $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; и $mail->Port = 587;.
Как понять, что сработало. В консоли появится «Письмо отправлено», а в ящике получателя — само письмо. Если пусто — смотрите папку «Спам» и раздел про ошибки ниже.
Функция mail() возвращает true не тогда, когда письмо дошло, а когда его приняла локальная очередь. Это разные события, и путать их — причина половины вопросов «почему письмо не пришло, хотя всё работает».
Шаг 4. Включите отладку, если письмо не доходит
Добавьте перед $mail->send() строку, и PHPMailer выведет весь диалог с SMTP-сервером — по нему видно, на каком шаге всё сломалось:
$mail->SMTPDebug = SMTP::DEBUG_SERVER; // подробный лог обмена с сервером
В продакшене эту строку уберите — она печатает служебные данные в вывод.
Если не получилось
Пять ошибок, которые встречаются чаще всего, и что за ними стоит:
- SMTP connect() failed — не удалось соединиться с сервером. Обычно неверный
HostилиPort, либо порт закрыт файрволом хостинга. Многие хостинги блокируют исходящие 25/465/587 — уточните у поддержки, какие открыты. - SMTP Error: Could not authenticate — сервер отверг логин или пароль. Чаще всего вы подставили основной пароль вместо пароля приложения. Создайте пароль приложения в настройках безопасности почты.
- Could not instantiate mail function — это ошибка способа 1: локальный sendmail не настроен. Переходите на SMTP.
- Письмо ушло, но в спам. Проверьте, что домен в
setFromсовпадает с доменом SMTP-аккаунта, и что у домена настроены записи SPF и DKIM. Отправка «от чужого имени» почти всегда режется. - Кракозябры вместо кириллицы. Не задан
$mail->CharSet = 'UTF-8';либо сам файл скрипта сохранён не в UTF-8.
Что делать дальше
Первое письмо ушло — дальше стоит убрать из кода пароль. Хранить его прямо в скрипте нельзя: вынесите в переменные окружения или отдельный конфиг вне корня сайта и вне репозитория. Второе — не отправляйте письма синхронно в момент запроса пользователя: если SMTP тормозит, тормозит и ваш сайт. Ставьте отправку в очередь (например, через фоновый воркер) — но это уже отдельная тема, здесь мы её только обозначаем. И третье: для массовых или транзакционных писем со временем переходят на API почтовых сервисов вместо SMTP — так проще следить за доставляемостью.
Разбор научной статьи: Сложный текст — понятным языком, с терминами
Спросить за 12 ₽Источники: PHPMailer — репозиторий и документация, PHP: mail — Руководство по PHP
Частые вопросы
Работает ли этот код на PHP 7.4?
У меня не порт 465, а 587 — что менять?
Отправка вернула успех, но письмо не пришло. Почему?
Можно ли не ставить PHPMailer и обойтись mail()?
Хостинг выдаёт SMTP connect() failed, хотя доступы верные. Что делать?
Куда деть пароль, чтобы он не попал в репозиторий?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.