Почта в Laravel построена на Symfony Mailer, поэтому подключение к SMTP-релею — это шесть строк в .env. Ниже настройка fmailer и то, что нужно приложению дальше: классы Mailable, Markdown-шаблоны, вложения, отправка через очередь и тесты без реальной доставки.
| Параметр | Значение |
|---|---|
| SMTP-хост | smtp.fmailer.ru |
| Порт | 587 |
| Шифрование | STARTTLS (соединение переводится в TLS до авторизации) |
| Логин | Логин SMTP-токена вашего домена |
| Пароль | Пароль этого токена — показывается один раз, при создании |
MAIL_MAILER=smtp
MAIL_HOST=smtp.fmailer.ru
MAIL_PORT=587
MAIL_ENCRYPTION=tls
MAIL_USERNAME=<логин токена>
MAIL_PASSWORD=<пароль токена>
MAIL_FROM_ADDRESS=noreply@mail.example.ru
MAIL_FROM_NAME="${APP_NAME}"MAIL_ENCRYPTION=tls означает STARTTLS, а не шифрование с первого байта — это давняя путаница в названии. Для порта 465 нужно MAIL_ENCRYPTION=ssl. В Laravel 11 это заменили явной схемой:
# Laravel 11 и новее понимают и схему целиком
MAIL_SCHEME=smtp # "smtps" — шифрование сразу, порт 465
MAIL_HOST=smtp.fmailer.ru
MAIL_PORT=587.env ни на что не влияет, пока кэш не сброшен. Это самая частая причина, по которой правильные настройки почты «не работают». php artisan config:clear
php artisan queue:restart # воркеры держат старый конфиг в памятиСоздайте Mailable с Markdown-шаблоном:
php artisan make:mail OrderConfirmed --markdown=emails.orders.confirmed<?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 OrderConfirmed extends Mailable
{
use Queueable, SerializesModels;
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.confirmed');
}
}И отправьте через фасад Mail:
use App\Mail\OrderConfirmed;
use Illuminate\Support\Facades\Mail;
Mail::to($order->customer->email)->send(new OrderConfirmed($order));@mail.ru через сторонний релей не проходит DMARC и попадает в спам. SMTP-доставка медленная относительно HTTP-ответа: соединение, TLS-рукопожатие, авторизация, само письмо. Класс выше использует трейт Queueable, поэтому вынести отправку из запроса — это замена одного слова:
// Отправку берёт на себя воркер — HTTP-ответ уходит сразу.
Mail::to($order->customer->email)->queue(new OrderConfirmed($order));
// Или с задержкой
Mail::to($order->customer->email)->later(now()->addMinutes(10), new OrderConfirmed($order));Письмо из очереди ещё и повторяется автоматически, если SMTP-сервер ненадолго недоступен — при синхронной отправке это была бы ошибка 500.
use Illuminate\Mail\Mailables\Attachment;
public function attachments(): array
{
return [
Attachment::fromPath(storage_path('invoices/2026-03.pdf'))
->as('invoice.pdf')
->withMime('application/pdf'),
// или из данных, собранных в памяти
Attachment::fromData(fn () => $this->pdf, 'invoice.pdf')
->withMime('application/pdf'),
];
} Массив в Mail::to() кладёт все адреса в один заголовок To:, и получатели видят друг друга. Для писем клиентам — цикл:
// Отдельное письмо каждому — никто не видит чужих адресов.
foreach ($users as $user) {
Mail::to($user->email)->queue(new WeeklyReport($user));
}# .env.local — писать письма в storage/logs/laravel.log вместо отправки
MAIL_MAILER=loguse Illuminate\Support\Facades\Mail;
use App\Mail\OrderConfirmed;
Mail::fake();
$this->post('/orders', $payload);
Mail::assertQueued(OrderConfirmed::class, fn ($mail) => $mail->order->is($order));| Ошибка | Причина и решение |
|---|---|
Настройки не применяются после правки .env | Кэш конфигурации. php artisan config:clear и queue:restart. |
Expected response code 250 but got code 535 | Неверные MAIL_USERNAME / MAIL_PASSWORD. |
Connection could not be established | Порт заблокирован хостингом. Используйте 8587 или 8465. |
stream_socket_enable_crypto(): SSL operation failed | ssl на порту 587 или tls на 465 — приведите в соответствие. |
| Код ответа 550 | Домен в MAIL_FROM_ADDRESS не подтверждён. |
| Письма из очереди не уходят | Не запущен воркер. Проверьте queue:work и таблицу failed_jobs. |
Laravel кэширует конфигурацию. Выполните php artisan config:clear, а в продакшене — заново php artisan config:cache. Воркеры очередей тоже держат старый конфиг в памяти, их нужно перезапустить командой php artisan queue:restart.
tls вместе с MAIL_PORT=587: несмотря на название, это STARTTLS. ssl вместе с MAIL_PORT=465 — шифрование с первого байта. В Laravel 11 и новее вместо этого можно задать MAIL_SCHEME со значением smtp или smtps и не указывать MAIL_ENCRYPTION вовсе.
535 — отказ авторизации: неверный MAIL_USERNAME или MAIL_PASSWORD. Нужен полный логин SMTP-токена домена и пароль, показанный при его создании. Если в пароле есть решётка или пробел, возьмите значение в кавычки прямо в .env.
Большинство хостингов блокируют исходящие порты 25, 465 и 587. Поставьте MAIL_PORT=8587 для STARTTLS или 8465 для шифрования с первого байта.
Используйте Mail::queue() вместо Mail::send() и запустите воркер очереди. Mailable сериализуется в очередь и отправляется воркером, поэтому запрос не ждёт SMTP, а неудачная отправка повторяется вместо ошибки 500.
Верните Mailable прямо из роута — Route::get('/preview', fn () => new OrderConfirmed($order)) — и Laravel отрисует его в браузере. Чтобы перехватывать реальные отправки в разработке, поставьте MAIL_MAILER=log: письмо целиком попадёт в storage/logs/laravel.log.