Django и транзакционные письма: настройка SMTP за 20 минут
Пошаговая настройка отправки писем из Django через транзакционный сервис по SMTP: от переменных окружения до проверки доставки. К концу вы отправите первое письмо из shell.

К концу этой инструкции Django будет отправлять письма через транзакционный сервис (Postmark, Mailgun, SendGrid, Brevo или похожий) по протоколу SMTP. Вы настроите подключение через переменные окружения, отправите тестовое письмо из manage.py shell и увидите его в панели сервиса. На всё уйдёт 15–20 минут, если аккаунт в сервисе уже заведён. Пример написан для Django 5.x, но настройки почты не менялись с Django 3.x, так что подойдёт и для более старых версий.
Транзакционный сервис нужен вот зачем: если отправлять письма напрямую с вашего сервера, они почти гарантированно уйдут в спам или будут отклонены — у нового IP нет репутации, нет настроенных SPF, DKIM и DMARC. Сервис берёт доставляемость на себя.
Что нужно иметь до начала
- Проект на Django 5.x с установленным окружением (виртуальное окружение активировано,
python manage.py runserverзапускается). - Аккаунт в транзакционном сервисе с подтверждённым доменом-отправителем. Пока домен не подтверждён (не прописаны DNS-записи SPF и DKIM), письма отправляться не будут — это делается в панели сервиса, до настройки Django.
- SMTP-доступы от сервиса: хост, порт, логин, пароль (или API-ключ, который в SMTP подставляется как пароль). Их выдают в разделе вроде «SMTP settings» или «Sending → SMTP».
- Установленный пакет
python-decoupleилиdjango-environдля чтения переменных окружения — чтобы не хранить пароль в коде. В примере используемpython-decouple.
Установите библиотеку для переменных окружения, если её ещё нет:
pip install python-decouple
Django умеет отправлять почту через SMTP из коробки — отдельный пакет для самой отправки ставить не нужно.
Шаг 1. Соберите SMTP-доступы сервиса
Откройте в панели сервиса раздел с SMTP-настройками. Вам нужны четыре значения. Названия у разных сервисов отличаются, но суть одна:
| Что нужно | Типичное значение | Куда пойдёт в Django |
|---|---|---|
| SMTP-хост | smtp.provider.com | EMAIL_HOST |
| Порт | 587 (STARTTLS) или 465 (SSL) | EMAIL_PORT |
| Логин | строка или API-ключ от сервиса | EMAIL_HOST_USER |
| Пароль | пароль SMTP или тот же API-ключ | EMAIL_HOST_PASSWORD |
Точные хост и порт смотрите в документации вашего сервиса — они у всех свои. Порт 587 работает с STARTTLS (в Django это EMAIL_USE_TLS=True), порт 465 — с прямым SSL (EMAIL_USE_SSL=True). Одновременно включать оба нельзя.
Проверка: у вас на руках четыре значения и вы знаете, какой порт использовать.
Шаг 2. Положите доступы в .env, а не в код
Создайте файл .env в корне проекта (рядом с manage.py) и добавьте его в .gitignore, чтобы пароль не попал в репозиторий:
# .env
EMAIL_HOST=smtp.provider.com
EMAIL_PORT=587
EMAIL_HOST_USER=YOUR_SMTP_LOGIN
EMAIL_HOST_PASSWORD=YOUR_SMTP_PASSWORD
EMAIL_USE_TLS=True
DEFAULT_FROM_EMAIL=noreply@ваш-домен.ru
Адрес в DEFAULT_FROM_EMAIL должен быть на том домене, который вы подтвердили в сервисе. Если отправлять с чужого или неподтверждённого домена — сервис отклонит письмо.
Проверка: файл .env есть, и команда git status его не показывает как новый файл к коммиту.
Шаг 3. Пропишите настройки в settings.py
В settings.py прочитайте значения из окружения и настройте бэкенд SMTP:
# settings.py
from decouple import config
# Бэкенд SMTP — стандартный для реальной отправки
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = config("EMAIL_HOST")
EMAIL_PORT = config("EMAIL_PORT", cast=int)
EMAIL_HOST_USER = config("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = config("EMAIL_HOST_PASSWORD")
EMAIL_USE_TLS = config("EMAIL_USE_TLS", default=True, cast=bool)
# Адрес по умолчанию для писем без явного from_email
DEFAULT_FROM_EMAIL = config("DEFAULT_FROM_EMAIL")
Здесь cast=int и cast=bool нужны потому, что переменные окружения — всегда строки, а Django ждёт число для порта и булево значение для TLS.
Проверка: python manage.py check проходит без ошибок, сервер запускается.
Шаг 4. Отправьте тестовое письмо из shell
Запустите интерактивную оболочку и отправьте письмо:
python manage.py shell
Внутри оболочки:
from django.core.mail import send_mail
otpravleno = send_mail(
subject="Тестовое письмо из Django",
message="Если вы это читаете, SMTP настроен верно.",
from_email=None, # None возьмёт DEFAULT_FROM_EMAIL
recipient_list=["ваш-личный-адрес@example.com"],
fail_silently=False, # покажет ошибку, если отправка не удалась
)
print(otpravleno) # ожидаем 1 — столько писем ушло
Флаг fail_silently=False важен на этапе настройки: при проблеме вы увидите исключение с текстом, а не тихий провал. В боевом коде его иногда ставят в True, чтобы падение почты не роняло запрос — но тогда логируйте результат сами.
Проверка: print вывел 1, письмо пришло на указанный адрес, а в панели сервиса появилась запись об отправке со статусом «delivered» или «sent».
Шаг 5. Письмо с HTML и текстовой версией
Транзакционные письма обычно верстают в HTML, но хорошая практика — прикладывать и текстовую версию. Для этого используйте EmailMultiAlternatives:
from django.core.mail import EmailMultiAlternatives
tekst = "Спасибо за регистрацию. Подтвердите почту по ссылке."
html = "Спасибо за регистрацию. Подтвердите почту.
"
pismo = EmailMultiAlternatives(
subject="Подтверждение регистрации",
body=tekst, # текстовая версия
from_email=None,
to=["user@example.com"],
)
pismo.attach_alternative(html, "text/html") # HTML-версия
pismo.send()
Почтовый клиент сам выберет, что показать. Это же снижает шанс попасть в спам: письма без текстовой версии фильтры любят меньше.
Никогда не собирайте HTML письма конкатенацией строк с пользовательскими данными — используйте шаблоны Django и render_to_string, иначе получите дыру с внедрением разметки в письмо.
Если не получилось
- SMTPAuthenticationError: (535, ...) — неверный логин или пароль. Проверьте, что
EMAIL_HOST_USERиEMAIL_HOST_PASSWORD— именно SMTP-доступы, а не логин от личного кабинета сервиса. У многих сервисов пароль SMTP — это отдельный сгенерированный ключ. - SMTPServerDisconnected или таймаут при подключении — не совпали порт и режим шифрования. На порту 587 нужен
EMAIL_USE_TLS=True, на 465 —EMAIL_USE_SSL=True. Оба сразу включать нельзя. - Письмо отправилось (вернулась 1), но не пришло — смотрите в панели сервиса статус. Частая причина: домен-отправитель не подтверждён или адрес получателя попал в suppression-список после прошлого отскока.
- SMTPSenderRefused: (550, ...) — адрес в
from_emailна неподтверждённом домене. Отправляйте только с домена, для которого настроены DKIM и SPF. - Connection refused на localhost — Django не увидел ваши настройки и пытается стучаться на локальный SMTP. Значит,
EMAIL_HOSTне подхватился: проверьте, что.envлежит рядом сmanage.pyиconfig()читает нужное имя переменной.
Что сделать дальше
Отправка через send_mail внутри HTTP-запроса блокирует ответ пользователю на время связи с SMTP-сервером — это доли секунды в норме, но секунды при проблемах на стороне сервиса. Следующий шаг — вынести отправку в фоновую задачу через Celery или Django-Q, чтобы регистрация не зависала из-за медленной почты. Это отдельная тема, здесь мы её только обозначаем.
Ещё две вещи стоит настроить до боевого запуска: логирование результата отправки (чтобы видеть отказы) и вебхуки от сервиса об отскоках и жалобах на спам — по ним чистят базу адресов. Для локальной разработки удобно переключить бэкенд на django.core.mail.backends.console.EmailBackend — тогда письма будут печататься в консоль, а не уходить наружу.
Проверка рекламы на риски: Что в макете вызовет вопросы ФАС: превосходные степени, сравнения, обещания результата, маркировка.
Спросить за 40 ₽Источники: Django documentation: Sending email, Django settings reference: EMAIL_BACKEND и связанные
Частые вопросы
А если у меня Django 3 или 4, инструкция подойдёт?
Письмо вернуло 1, но так и не дошло. Куда смотреть?
Обязательно ли выносить пароль в .env, или можно прямо в settings.py?
Порт 587 или 465 — что выбрать?
Можно ли тестировать отправку, не тратя лимит сервиса?
Стоит ли отправлять письма прямо в обработчике запроса?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.