SMTP Яндекса в Laravel и подтверждение подписки: подводные камни
Почта ♡
и слава богу ♡
Для рассылки мне нужна была почта, которая не уходит в спам и не требует отдельного сервиса за деньги. Проще всего было взять обычный ящик Яндекса. Настройка заняла пять минут, а потом ещё час ушёл на ошибку "535 5.7.8", при которой пароль вроде бы верный, а доступа нет. Складываю всё в одну статью, чтобы ты этот час не потеряла.
Настройки в .env
Для Яндекса нужен SMTP-сервер на порту 465 с шифрованием. В свежем Laravel это задаётся схемой "smtps":
MAIL_MAILER=smtp
MAIL_HOST=smtp.yandex.ru
MAIL_PORT=465
MAIL_SCHEME=smtps
MAIL_USERNAME=ваш-ящик@yandex.ru
MAIL_PASSWORD=пароль-приложения
MAIL_FROM_ADDRESS="ваш-ящик@yandex.ru"
Адрес отправителя должен совпадать с логином, иначе Яндекс письмо не примет. И обрати внимание: "MAIL_PASSWORD" это не пароль от почты, а специальный пароль приложения. Если вписать обычный, будет та самая ошибка, о которой ниже.
Ошибка 535: у пользователя нет доступа
Проверять отправку лучше одной командой, без форм и очередей, чтобы не гадать, что из них сломалось:
php artisan tinker --execute 'Mail::raw("Проверка почты", fn ($m) => $m->to("me@example.com")->subject("Тест"));'
Получаю в ответ:
535 5.7.8 Error: authentication failed: This user does not have access rights to this service
Пароль правильный, логин правильный, а почта не пускает. Начинается любимая игра «что я сделала не так». Причина оказалась в самом ящике Яндекса: доступ сторонних программ там по умолчанию отключён. Лечится в настройках почты: «Почтовые программы», включить доступ по протоколу IMAP и отметить «Пароли приложений и OAuth-токены». Сам пароль приложения создаётся отдельно в Яндекс ID, в разделе «Безопасность», для типа «Почта».
Как устроено подтверждение подписки
Подписчик получает письмо со ссылкой, а адрес становится активным только после клика. У каждого свой токен. Повторное письмо с подтверждением я не отправляю чаще чем раз в 10 минут, чтобы форму нельзя было использовать для рассылки спама другим людям. А если адрес уже подтверждён, ответ не меняется, чтобы по нему нельзя было вычислить, кто подписан.
Про сам путь «письмо, ссылка, страница» и про то, как тест нашёл в нём дыру, читай в статье про подписку.
Рассылка новых записей без очередей
Отправлять письма о новых записях можно без сложных очередей и Redis. В планировщике стоит команда раз в пять минут:
Schedule::command('subscribers:notify')->everyFiveMinutes()->withoutOverlapping();
Чтобы одна запись не разошлась дважды (представь, как это выглядит для подписчика), она «занимается» одним обновлением поля в базе:
$claimed = DiaryPost::query()->whereKey($post->id)->whereNull('notified_at')->update(['notified_at' => now()]);
if ($claimed === 0) {
continue;
}
Если два процесса возьмут одну запись, обновление сработает только у первого, а второй получит ноль и пойдёт дальше. Записи с датой в будущем дожидаются своего времени сами: планировщик проверяет, вышла ли запись, и только тогда рассылает письма. Именно так у меня работают отложенные публикации.
Лимиты Яндекса
С обычного ящика можно отправлять порядка 500 писем в сутки. Для маленького блога этого хватает с огромным запасом. Когда подписчиков станет больше, придётся переходить на отдельный сервис рассылок, но это такая проблема, которую приятно иметь.
Что стоит запомнить
Ошибка 535 почти всегда про настройки самого ящика, а не про твой код. Проверяй отправку отдельной командой, до того как в дело вступят формы и очереди. И держи пароль приложения только в ".env", а не в репозитории: гитхаб такое не прощает. Как проект выкладывается на сервер и где хранится ".env", я описала в статье про деплой Laravel.
Комментарии
Буду рада вашим мыслям, идеям и вопросам!
Давайте обсуждать ♡
Делитесь
своими мыслями
— мне очень
это важно ♡