Отправка писем в Go через net/smtp

Введение
Иногда нужно автоматически отправлять письма из программы — для подтверждений, уведомлений, отчётов или аутентификации. SMTP (Simple Mail Transfer Protocol) — стандарт для отправки электронной почты между серверами, а IMAP/POP3 — для получения. Go включает базовую поддержку SMTP в пакете net/smtp и дополнительно использует TLS и стандартную криптографию для безопасного соединения.
Важно: net/smtp предоставляет низкоуровневые инструменты — для сложных сценариев массовых рассылок или удобной обработки HTML/вложений лучше рассмотреть внешние библиотеки или сервисы.
Что нужно знать перед началом
- Понимание SMTP: отправитель, получатель, хост (сервер), порт и тело сообщения.
- Учетная запись почты и пароль приложения (app password) для провайдеров, требующих отдельный пароль для внешних клиентов (например, Yahoo).
- Доступ к порту и хосту SMTP у вашего MSP (Mail Service Provider).
Определения:
- SMTP: протокол отправки писем между серверами.
- TLS: шифрование транспортного уровня, защищает соединение.
TL;DR кода
Привожу компактную рабочую реализацию SendMail() для отправки простого текстового письма через SMTP с TLS.
package main
import (
"crypto/tls"
"fmt"
"log"
"net/smtp"
)
func main() {
fmt.Println(SendMail())
}
func SendMail() string {
from := "youremail@yahoo.com"
password := "yourAppPasswordHere"
to := "recipient@example.com"
host := "smtp.mail.yahoo.com"
port := "465"
serverAddress := host + ":" + port
subject := "Пример: проверка отправки"
body := "Здравствуйте! Это тестовое письмо, отправленное с Go."
// Заголовки
headers := make(map[string]string)
headers["From"] = from
headers["To"] = to
headers["Subject"] = subject
message := ""
for k, v := range headers {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + body
// Аутентификация
auth := smtp.PlainAuth("", from, password, host)
// TLS-конфигурация
tlsConfig := &tls.Config{
InsecureSkipVerify: true, // В production рекомендуется проверить сертификат сервера
ServerName: host,
}
// Установка TCP/TLS соединения
conn, err := tls.Dial("tcp", serverAddress, tlsConfig)
if err != nil {
log.Panic(err)
}
client, err := smtp.NewClient(conn, host)
if err != nil {
log.Panic(err)
}
// Аутентификация на SMTP-сервере
if err = client.Auth(auth); err != nil {
log.Panic(err)
}
// Отправитель и получатель
if err = client.Mail(from); err != nil {
log.Panic(err)
}
if err = client.Rcpt(to); err != nil {
log.Panic(err)
}
// Запись тела письма
w, err := client.Data()
if err != nil {
log.Panic(err)
}
_, err = w.Write([]byte(message))
if err != nil {
log.Panic(err)
}
if err = w.Close(); err != nil {
log.Panic(err)
}
// Закрываем соединение корректно
if err = client.Quit(); err != nil {
log.Panic(err)
}
return "Успешно: письмо отправлено"
}Важно: в примере InsecureSkipVerify=true — это удобство для тестов и локальной отладки. В продакшне нужно валидировать сертификат сервера или использовать доверенный корневой сертификат.
Разбор ключевых частей решения
- Импорты
- crypto/tls — конфигурация TLS для защищённого соединения.
- net/smtp — базовый SMTP-клиент.
- log, fmt — для логирования и форматирования.
- Заголовки и тело
SMTP ожидает текст сообщения с заголовками в формате “Header: Value” и пустой строкой перед телом. Наш код собирает map[string]string заголовков и формирует message.
- Аутентификация
smtp.PlainAuth идентифицирует клиента. Аргументы: identity (можно пустой), username (обычно адрес), password, host.
- TLS + Dial
Многие провайдеры требуют шифрование на уровне транспорта. Мы создаём tls.Config с ServerName и устанавливаем соединение через tls.Dial.
- smtp.NewClient
Нужен для выполнения SMTP-команд: Auth, Mail, Rcpt, Data, Quit.
- Ошибки
Каждый шаг должен проверяться. Для продакшна вместо log.Panic используйте более мягкую обработку — повторные попытки (retry), логирование в систему мониторинга или отчёт об ошибке.
Настройка для Yahoo (и других провайдеров)
- Для Yahoo: нужно создать пароль приложения в Account Security → Generate app password. Этот пароль используется вместо основного пароля аккаунта в коде.
Хост и порт Yahoo в примере: smtp.mail.yahoo.com:465 (порт 465 — SMTPS по TLS). Провайдеры могут использовать другие порты: 587 (STARTTLS) или 25.
Gmail: Google ограничил «менее безопасные приложения». Для Gmail используйте OAuth2 или пароль приложения при включённом 2FA.
Отправка нескольким получателям и массив адресов
Если нужно отправлять письма группе получателей, используйте срез строк и вызов client.Rcpt для каждого адреса.
Пример:
recipients := []string{"a@example.com", "b@example.com"}
for _, rcpt := range recipients {
if err = client.Rcpt(rcpt); err != nil {
log.Panic(err)
}
}Когда net/smtp не подходит (примеры)
- Нужна отправка HTML с вложениями и многоформатная обработка — net/smtp требует ручной сборки MIME-частей.
- Массовые рассылки с отслеживанием и аналитикой — лучше сервисы типа SendGrid, Mailgun или SMTP API провайдеров.
- Аутентификация по OAuth2 (например, Gmail API): net/smtp с PlainAuth не покрывает OAuth2.
Альтернативные подходы
- Использовать готовые Go-библиотеки: gomail (github.com/go-gomail/gomail), mailgun-go, sendgrid-go.
- API внешних сервисов: SendGrid, Mailgun, Amazon SES — удобно для масштабирования и аналитики.
- Gmail API + OAuth2 для отправки через Gmail с современной авторизацией.
Безопасность и соответствие требованиям
- Никогда не хардкодьте реальные пароли в репозитории. Используйте переменные окружения или секреты (Vault, AWS Secrets Manager).
- В production включите проверку сертификатов TLS (InsecureSkipVerify=false) и применяйте ограничение доверенных корней.
- Хранение и обработка персональных данных: если отправляете личные данные, убедитесь в соблюдении GDPR/локального законодательства — минимизация данных, шифрование, аудит.
Чек-листы по ролям
Разработчик:
- Использует переменные окружения для секретов
- Добавил обработку ошибок и retries
- Покрыл unit-тестами шаблоны сообщений
Операции (Ops):
- Настроены DNS / PTR / SPF / DKIM / DMARC для домена отправителя
- Мониторинг отказов и очередей
- Лимиты отправки и rate limiting
Информационная безопасность:
- TLS с проверкой сертификата
- Аудит логов отправки
- Контроль доступа к секретам
Продукт / Менеджмент:
- Согласован формат писем с поддержкой и дизайном
- Планы на обработку отписок и жалоб на спам
Мини-методология развёртывания
- Разработать локальную реализацию и протестировать с тестовой почтой.
- Вынести секреты в безопасное хранилище.
- Настроить мониторинг (удачные/неудачные попытки, latency).
- Запускать через очередь задач (background worker) для устойчивости.
- Обеспечить откат и фолбек на внешний сервис при массовых ошибках.
Диагностика и частые ошибки
- “535 Authentication failed” — неверный пароль или неправильный формат аутентификации.
- “connection reset” или “certificate error” — проблемы с TLS, проверьте ServerName и доверенные CA.
- Письма не доходят — проверьте SPF/DKIM/DMARC, и не попадание в спам-фильтры.
Пример потока решений (Mermaid)
flowchart TD
A[Нужно отправить почту?] --> B{Объём и требования}
B -->|Один раз / небольшие уведомления| C[Использовать net/smtp]
B -->|HTML/вложения/массовые рассылки| D[Использовать внешнюю библиотеку или API]
D --> E[SendGrid / Mailgun / Amazon SES]
C --> F[Настроить TLS и аутентификацию]
F --> G[Тестирование и мониторинг]Критерии приёмки
- Письмо успешно отправляется в тестовой среде и приходит на почтовый ящик получателя.
- Секреты вынесены и не попадают в репозиторий.
- TLS в продакшне валидируется, логиируют ошибки и метрики.
Краткая таблица совместимости и подсказки по миграции
- Yahoo: smtp.mail.yahoo.com, порт 465 (TLS) или 587 (STARTTLS), требуется пароль приложения.
- Gmail: порт 587 (STARTTLS) или Gmail API с OAuth2; пароль приложения возможен только при 2FA и включенных настройках.
- Outlook/Office365: smtp.office365.com, порт 587 (STARTTLS).
При миграции на API-сервис (SendGrid и пр.) обычно меняется код отправки на HTTP-запросы и добавляется поддержка более надёжной обработки ошибок и аналитики.
Шаблон простого плейбука для инцидента: “Письма не отправляются”
- Проверить логи приложения — ошибки соединения, аутентификация.
- Проверить доступность хоста (ping/telnet host:port).
- Проверить сроки действия/смену паролей приложений.
- Проверить изменения в политике провайдера (например, ограничение по IP).
- При массовых отказах переключиться на резервный SMTP/API сервис.
Резюме
net/smtp — простой и прозрачный инструмент для отправки базовых писем из Go. Для прототипа и небольших задач он отлично подходит. Для более сложных требований (HTML, вложения, массовые рассылки, аналитика, OAuth2) рассмотрите дополнительные библиотеки или SaaS-решения. Всегда используйте безопасное хранение секретов и корректную проверку TLS в продакшне.
Важные заметки:
- Используйте пароль приложения у поставщиков, которые их поддерживают (Yahoo, Gmail с 2FA).
- Не игнорируйте проверку сертификатов TLS в production.
Конец статьи.
Похожие материалы
Как перестать откладывать решения
Просмотр метаданных фото на iPhone (iOS 15)
Как воспроизвести два видео одновременно в VLC
Split View на Mac — разделение экрана и советы
Конвертация видео в GIF на Linux