Отправка почты из Laravel: разбор на живом примере
Как настроить почту в Laravel 11, собрать письмо через Mailable, отправить в очередь и не спалить SMTP-лимиты. Разбираем частые ошибки: письмо не уходит, падает в спам, очередь молчит.

Пользователь регистрируется — ему должно прийти письмо. На локальной машине всё работает, на проде письмо не доходит, а в логах тишина. Разберём отправку почты в Laravel 11 на сквозном примере: настройка драйвера, письмо через класс Mailable, вложение, шаблон и вынос в очередь. Покажу, где обычно ломается и что смотреть первым.
Код проверялся на Laravel 11.x с PHP 8.2. В Laravel 9 система почты уже переехала с SwiftMailer на Symfony Mailer, поэтому синтаксис Mailable ниже актуален для 9.x, 10.x и 11.x. Если у вас 8.x — многое отличается, сверяйтесь с документацией своей версии.
Что настроить до отправки первого письма
Laravel не отправляет письма напрямую — он передаёт их через транспорт (mailer), заданный в конфиге. Настройки живут в config/mail.php, а значения подтягиваются из .env. Минимальный рабочий набор переменных для SMTP:
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=YOUR_SMTP_LOGIN
MAIL_PASSWORD=YOUR_SMTP_PASSWORD
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="noreply@example.com"
MAIL_FROM_NAME="${APP_NAME}"MAIL_FROM_ADDRESS должен совпадать с доменом, для которого у вас настроены SPF и DKIM-записи. Если письмо уходит от noreply@example.com, а DKIM подписан для другого домена — почта уйдёт в спам, и в логах Laravel вы этого не увидите: с точки зрения приложения письмо отправлено успешно.
«Письмо отправлено» в логах Laravel означает только одно: SMTP-сервер принял сообщение. Дошло ли оно до ящика и не легло ли в спам — за пределами зоны видимости приложения.
Драйвер log для локальной разработки
Пока настраиваете шаблоны, не отправляйте реальные письма. Поставьте MAIL_MAILER=log — тогда всё письмо целиком пишется в storage/logs/laravel.log, включая HTML-тело. Удобно проверять вёрстку, не засоряя чужие ящики. Для просмотра писем в браузере многие используют Mailpit или Mailtrap, но это уже внешние инструменты.
Собираем письмо: класс Mailable
Каждое письмо в Laravel — отдельный класс. Создаём командой Artisan:
php artisan make:mail OrderShipped --markdown=emails.orders.shippedФлаг --markdown создаёт заодно и шаблон с готовой вёрсткой писем. Класс появится в app/Mail/OrderShipped.php. Вот рабочий пример письма о том, что заказ отправлен:
<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
// Модель заказа доступна в шаблоне как $order
public function __construct(public Order $order)
{
}
// Тема письма и отправитель
public function envelope(): Envelope
{
return new Envelope(
subject: 'Заказ №' . $this->order->id . ' отправлен',
);
}
// Шаблон и переменные
public function content(): Content
{
return new Content(
markdown: 'emails.orders.shipped',
);
}
}Публичное свойство $order в конструкторе автоматически доступно в шаблоне — отдельно передавать через with() не нужно. Это поведение появилось в Laravel 9 вместе с новым API: методы build(), subject и view() заменены на envelope(), content() и attachments().
Шаблон письма
Файл resources/views/emails/orders/shipped.blade.php при генерации с --markdown использует компоненты Laravel для писем:
<x-mail::message>
# Заказ отправлен
Ваш заказ №{{ $order->id }} передан в доставку.
<x-mail::button :url="$order->tracking_url">
Отследить посылку
</x-mail::button>
Спасибо за покупку,<br>
{{ config('app.name') }}
</x-mail::component>Компонент x-mail::message оборачивает содержимое в готовую адаптивную вёрстку с инлайновыми стилями — почтовые клиенты не понимают внешние CSS-файлы, поэтому стили встраиваются прямо в теги.
Отправка
Отправляем письмо через фасад Mail. Вызов в контроллере после оформления заказа:
use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;
Mail::to($order->user->email)->send(new OrderShipped($order));Метод send() блокирующий: HTTP-запрос пользователя не завершится, пока Laravel не достучится до SMTP-сервера и не получит ответ. При медленном SMTP это добавляет к каждому запросу секунды. Решение — очередь.
Выносим в очередь
Чтобы письмо уходило в фоне, добавьте классу интерфейс ShouldQueue:
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderShipped extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
// ...
}После этого тот же вызов Mail::to(...)->send(...) не отправит письмо сразу, а положит задачу в очередь. Никакой замены на queue() в коде отправки не требуется — трейт SerializesModels сериализует модель Order по её ключу и восстановит из базы в момент обработки.
Дальше нужен воркер, который очередь разбирает. Настройте драйвер очереди в .env (QUEUE_CONNECTION=database для старта — таблицу создаёт миграция из php artisan queue:table) и запустите:
php artisan queue:workВ проде queue:work держат под супервизором — например, Supervisor или systemd, — чтобы процесс перезапускался после падения. Иначе после первой же ошибки воркер молча умрёт и письма перестанут уходить.
Частая ловушка очереди
Если письма перестали отправляться после деплоя нового кода — воркер работает со старой версией классов. Процесс queue:work загружает код один раз при старте и держит в памяти. После каждого деплоя нужно перезапускать воркеры:
php artisan queue:restartСравнение способов отправки
| Способ | Когда использовать | Цена |
|---|---|---|
Mail::send() без очереди | Редкие письма, локальная разработка | Пользователь ждёт ответа SMTP |
ShouldQueue + queue:work | Прод, любые письма пользователям | Нужен воркер под супервизором |
Mail::later() | Отложенная отправка (напоминания) | Тоже требует воркер |
Если письмо не уходит
- В логах «отправлено», но письма нет. С драйвером
logписьмо только пишется в файл — проверьте, чтоMAIL_MAILER=smtp, а неlog. И сбросьте кеш конфига:php artisan config:clear, иначе Laravel читает старый.env. - Connection could not be established / Connection timed out. Неверный хост, порт или блокировка исходящих SMTP-портов на сервере. Проверьте
MAIL_PORT(587 для TLS, 465 для SSL) и доступность порта с сервера черезtelnet. - Expected response code 250 but got 535. Ошибка авторизации: неверный логин или пароль SMTP. Многие провайдеры требуют отдельный пароль приложения, а не пароль от аккаунта.
- С очередью письма молчат. Не запущен
queue:workили после деплоя не сделанqueue:restart. Проверьте таблицуfailed_jobs— упавшие задачи оседают там с текстом ошибки. - Письма уходят в спам. Нет SPF/DKIM для домена в
MAIL_FROM_ADDRESSили адрес отправителя не совпадает с настроенным доменом. Это не ошибка Laravel — настраивается на стороне DNS и почтового провайдера.
Что смотреть в проде
После настройки следите за таблицей failed_jobs — растущее число упавших задач означает проблему с SMTP или очередью. Метрика, которую стоит мерить, — задержка между постановкой письма в очередь и фактической отправкой: если воркеров мало, а писем много, очередь копится. Настройте retry-политику через --tries у воркера и логирование доставки на стороне почтового провайдера. Тема надёжной доставки и репутации домена — отдельный разговор, здесь мы разобрали только код приложения.
Репетитор 1—11 класс и ВУЗ: Математика и физика методом наведения на мысль, помощь с ДЗ.
Спросить за 8 ₽Источники: Laravel 11 — Mail (официальная документация), Laravel 11 — Queues (официальная документация)
Частые вопросы
У меня Laravel 8, этот код не работает — почему?
Письмо уходит, но приходит в спам. Это баг Laravel?
Что будет, если я не запущу queue:work, а письмо помечено ShouldQueue?
Как проверить отправку локально, не рассылая реальные письма?
После деплоя письма перестали уходить, хотя раньше работали. Что случилось?
Обязательно ли выносить письма в очередь?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.