RE:NODE

Почта в Ghost: SMTP-релей, Mailgun, SPF и DKIM

Автор Команда RE:NODE
16 мин чтения


Ghost отправляет почту двумя независимыми путями, и настраиваются они по отдельности. Письма «один человеку» - ссылки для входа участников, приглашения сотрудников, сброс пароля - уходят через блок mail в конфигурации, обычно по SMTP, и подойдёт любой SMTP-релей. Массовые рассылки идут через Mailgun, который настраивается в Ghost Admin или в блоке bulkEmail, и по SMTP Ghost их не отправляет вообще. Каким бы путём ни шло письмо, до ящика оно доберётся, только если домен отправителя публикует записи SPF и DKIM, покрывающие тот сервис, который письмо отправляет. Большинство жалоб вида «почта в Ghost сломана» сводится к тому, что не хватает одного из этих трёх элементов.

Это руководство разбирает почту глубже, чем общее руководство по хостингу Ghost: точные ключи, рабочий пример с релеем, сторона Mailgun, записи DNS, проверку и расшифровку ошибок.

Два вида почты, два пути#

Полезно строго различать термины.

Транзакционная почта возникает, когда один человек что-то сделал, и уходит именно ему. В Ghost это:

  • Волшебная ссылка, по которой участник входит или подтверждает подписку. У участников Ghost нет пароля, поэтому эта ссылка - парадный вход.
  • Приглашения сотрудников и сброс паролей в админке.
  • Уведомления для сотрудников, например о новом участнике, если они включены.

Массовая почта - это рассылка: один пост уходит всем подписчикам из списка, часто тысячам писем за несколько минут.

Требования у них разные. Транзакционной почты мало, но она срочная: ссылка для входа, пришедшая через десять минут, уже потеряла читателя, поэтому нужен надёжный сервер отправки с хорошей репутацией. Массовой почте нужен сервис, созданный для рассылки: разбивка на пакеты, учёт по каждому получателю, отписки и обработка отказов. Эту работу Ghost передаёт Mailgun.

транзакционнаярассылкаодно письмопакетная отправкаGhostодин процесс Node.jsSMTP-релейблок mailMailgunbulkEmail или AdminЯщик участникассылка входаПодписчикирассылка
Каким путём идёт каждый вид почты Ghost

Пути независимы, поэтому один может работать, пока другой сломан. Сайт может отлично рассылать новости и при этом ни разу не доставить ссылку входа, и наоборот. Проверяйте оба.

Зачем Ghost нужен релей для транзакционной почты#

Если блок mail не настроен, Ghost пытается доставлять письма напрямую с самого сервера. То есть подключается к почтовому серверу каждого получателя на порт 25 с IP-адреса вашего сервера. В обычной хостинговой сети это не срабатывает сразу по двум причинам. Многие сети блокируют исходящий порт 25, чтобы остановить спам, а даже там, где он открыт, новый IP без обратной DNS-записи и без истории отправки вызывает у крупных почтовых провайдеров подозрение. Письмо либо не уходит, либо попадает в спам.

SMTP-релей решает обе проблемы. Ghost подключается к релею на порт подачи (587 или 465), входит под своей учётной записью и передаёт сообщение. Релей доставляет его с адреса, у которого уже есть репутация, и подписывает или заверяет письмо для вашего домена. SMTP-релей для исходящей почты объясняет идею в целом со стороны почтового сервера; для Ghost это просто «то, на что указывает блок mail».

Откуда взять релей, решаете вы:

  • Провайдер транзакционной почты. Сервисы, созданные для почты приложений, дают SMTP-хост, имя пользователя и ключ, а также панель, где видно, что доставлено и что вернулось. Это обычный ответ. Транзакционная почта из вашего приложения сравнивает, на что смотреть при выборе.
  • Исходящий сервер существующего почтового ящика. Подходит личному блогу с малым объёмом. Проверьте суточный лимит отправки и учтите, что некоторые провайдеры подменяют или ограничивают адрес From.
  • Собственный почтовый сервер. RE:NODE продаёт линейку Mail Server на базе Stalwart: SMTP, IMAP и JMAP и веб-админка для доменов, ящиков и DKIM. Ghost может отправлять на него письма как на любой другой релей. Настройка почтового сервера Stalwart описывает сервер, а руководство по собственному почтовому серверу честно говорит, что значит содержать такой сервер.
  • SMTP-точка Mailgun. Раз аккаунт в Mailgun вам всё равно нужен для рассылок, его SMTP-данные можно использовать и для транзакционной почты. Всё окажется в одной панели, но у одного поставщика.

Ghost всегда только подключается наружу к порту подачи. Порт 25 ему не нужен, и принимать почту он не умеет.

Блок mail: ключ за ключом#

Ghost читает конфигурацию из config.production.json в папке установки. Блок mail для типичного релея выглядит так:

config.production.json
{  "mail": {    "from": "'Example Blog' <noreply@example.com>",    "transport": "SMTP",    "options": {      "host": "smtp.relay.example",      "port": 587,      "secure": false,      "auth": {        "user": "smtp-username",        "pass": "smtp-password"      }    }  }}
КлючПримерЗначение
mail.transportSMTPИспользовать SMTP-сервер вместо прямой доставки
mail.from'Name' <noreply@example.com>Адрес отправителя по умолчанию
mail.options.hostsmtp.relay.exampleИмя хоста релея
mail.options.port587 или 465Порт подачи
mail.options.securefalse или truetrue только для порта 465
mail.options.auth.userлогинИмя пользователя SMTP
mail.options.auth.passпароль или ключПароль SMTP

Большинство проблем возникает из-за нескольких деталей.

Порт и `secure` идут в паре. Порт 465 использует TLS с первого байта, поэтому ему нужно "secure": true. Порт 587 начинает открытым текстом и переходит на шифрование командой STARTTLS, поэтому ему нужно "secure": false - переход всё равно происходит, флаг описывает лишь самый первый байт. Если их перепутать, будет тайм-аут или ошибка рукопожатия TLS, а не понятное сообщение.

Адрес `from` - это обещание. Релей обычно отправляет только от адресов или доменов, которые вы у него подтвердили, а получатели сверяют этот адрес с вашими записями SPF и DKIM. Берите адрес на домене, которым вы управляете и который настроили у релея. noreply@ годится для транзакционной почты, но настоящий, читаемый адрес добрее к читателю, который ответит на письмо со ссылкой входа.

Имя в кавычках. Одинарные кавычки внутри JSON-строки в from сделаны намеренно: "'Example Blog' <noreply@example.com>" связывает имя и адрес. Голый вариант Example Blog <noreply@example.com> в большинстве версий тоже работает, но в документации самого Ghost показана форма с кавычками.

У некоторых провайдеров есть именованный сервис. Почтовая библиотека под капотом принимает options.service с известным названием провайдера вместо хоста и порта, и документация Ghost показывает это для Mailgun. Если ваш провайдер указывает имя сервиса, не придётся вводить хост. Если сомневаетесь, пропишите host, port и secure явно - это работает всегда.

Настройка без правки файла

На машине, которой вы управляете через Ghost-CLI, ключ можно задать из оболочки, и Ghost-CLI сам запишет его в файл конфигурации:

bash
$ ghost config mail.transport SMTP$ ghost config mail.options.host smtp.relay.example$ ghost config mail.options.port 587$ ghost restart

Ghost читает и переменные окружения; двойное подчёркивание заменяет каждый уровень вложенности, так что mail__transport задаёт mail.transport:

env
mail__transport=SMTPmail__from='Example Blog' <noreply@example.com>mail__options__host=smtp.relay.examplemail__options__port=587mail__options__secure=falsemail__options__auth__user=smtp-usernamemail__options__auth__pass=smtp-password

Переменные окружения имеют приоритет над файлом. Здесь это полезно по конкретной причине: пароль SMTP - секрет, и хранить его в переменной окружения, а не в файле, который можно случайно выложить на форум или закоммитить в Git, - хорошая привычка. Переменные окружения и секреты рассматривают эту привычку в целом. Если правка файла как будто ничего не меняет, проверьте, не перекрывает ли её переменная окружения, и помните, что Ghost читает конфигурацию только при запуске.

На сервере Ghost в RE:NODE файл конфигурации - обычный файл в файловом менеджере и по SFTP, а на вкладке Startup лежат переменные окружения. Подойдёт любой путь; выберите тот, который запомните. После любого изменения перезапустите сервер.

Рассылки и Mailgun#

Отправка рассылок - другой механизм. Ghost берёт опубликованный пост, превращает его в письмо, делит список подписчиков на пакеты и отправляет эти пакеты в API Mailgun. Фактическую доставку в ящики выполняет Mailgun, он же сообщает Ghost об отказах и жалобах, а Ghost по ним помечает адреса как недоставляемые.

Настроить это можно двумя способами.

В Ghost Admin. В настройках письма рассылки вы вводите регион Mailgun, домен отправки и закрытый API-ключ Mailgun. Это обычный путь, и именно его описывает документация Ghost для самостоятельно размещённых сайтов.

В файле конфигурации. Блок bulkEmail хранит те же три значения, и документация Ghost называет его способом задать Mailgun из конфигурации:

config.production.json
{  "bulkEmail": {    "mailgun": {      "baseUrl": "https://api.mailgun.net/v3",      "apiKey": "your-mailgun-private-key",      "domain": "mg.example.com"    }  }}
КлючПримерЗначение
bulkEmail.mailgun.baseUrlhttps://api.mailgun.net/v3API Mailgun вашего региона
bulkEmail.mailgun.apiKeyзакрытый ключРазрешает Ghost отправку
bulkEmail.mailgun.domainmg.example.comДомен отправки, зарегистрированный в Mailgun

У Mailgun есть регионы США и ЕС, и аккаунт живёт в одном из них. Домену, созданному в регионе ЕС, нужен базовый адрес https://api.eu.mailgun.net/v3; если для европейского домена указать американский, получится ошибка авторизации, которая выглядит как неверный ключ, но на деле означает неверный регион. Если заданы оба варианта, файл конфигурации главнее того, что сохранено в админке, и это сбивает с толку, когда кто-то меняет одно и не меняет другое.

Домен отправки обычно поддомен вроде mg.example.com или news.example.com, а не основной домен. Так записи DNS, которые просит Mailgun, остаются отдельно от вашей обычной почты, а проблема с доставляемостью рассылок не тянет вниз адрес, с которого вы пишете деловую переписку. Адрес, от которого рассылка выглядит отправленной, всё равно может быть на основном домене; Ghost позволяет задать имя и адрес отправителя для каждой рассылки в пределах того, что Mailgun разрешит для этого домена.

Прежде чем на неё полагаться, отправьте проверочное письмо. Редактор рассылки Ghost умеет отправлять предпросмотр на ваш адрес, и это подтверждает, что ключ, регион и домен согласованы. Затем отправьте настоящий пост небольшому сегменту, например участникам с меткой, которую вы поставили нескольким знакомым, и только потом делайте первую полную рассылку.

Нужен ли вам Mailgun на самом деле

Только если вы хотите рассылать посты по почте. Сайту, который публикуется в вебе, пускает участников и никогда не рассылает новости, достаточно блока mail. Если вы не готовы настраивать Mailgun, подписчиков всё равно можно собирать, а рассылку отправить позже. Нельзя лишь заменить Mailgun своим SMTP-релеем для массовой отправки: если направить блок mail на релей, доставка рассылок не включится, какими бы щедрыми ни были лимиты релея.

SPF и DKIM для домена Ghost#

Получающий сервер проверяет почту с вашего домена по записям DNS, которые вы публикуете. Для Ghost важны две, а третья связывает их воедино.

SPF - это запись TXT на домене, где перечислены серверы, которым разрешено отправлять почту за этот домен. Получатель смотрит на адрес отправителя в конверте, берёт SPF-запись его домена и сверяет с ней IP, с которого пришло соединение.

DKIM - криптографическая подпись. Отправляющий сервис подписывает каждое письмо закрытым ключом; соответствующий открытый ключ вы публикуете в записи TXT по адресу selector._domainkey.yourdomain. Получатель берёт ключ и проверяет подпись, что доказывает: письмо не изменили, и отправил его тот, у кого есть ключ.

DMARC говорит, что делать, если проверки не пройдены, и требует, чтобы домен, прошедший SPF или DKIM, совпадал с видимым доменом From. Это называется выравниванием (alignment), и именно поэтому «SPF пройден» не всегда достаточно. SPF, DKIM и DMARC простыми словами разбирает все три по порядку.

Поскольку Ghost использует два сервиса, обычно получаются записи для обоих:

ИмяТипНазначение
example.comTXTSPF для транзакционного релея
selector._domainkey.example.comTXTКлюч DKIM для релея
mg.example.comTXTSPF для Mailgun
krs._domainkey.mg.example.comTXTКлюч DKIM для Mailgun
_dmarc.example.comTXTПолитика и адрес для отчётов

Точные имена селекторов и значения ключей приходят из панели каждого провайдера; не угадывайте их. Имя krs выше - лишь пример формы, копируйте то, что покажет Mailgun.

Минимальная запись SPF, разрешающая один релей, выглядит так:

Запись TXT на example.com
v=spf1 include:_spf.relay.example ~all

Правила, которые стоит знать:

  1. Одна запись SPF на имя. Если она у вас уже есть, добавьте новый include: в неё. Две записи SPF на одном имени приводят к постоянной ошибке проверки.
  2. Не больше десяти обращений к DNS. Каждый include: стоит как минимум одно, а вложенные - больше. Нагромоздите сервисов - и лимит будет превышен, после чего SPF перестанет проходить для всех.
  3. `~all` или `-all`. ~all означает «мягкий отказ всем, кого нет в списке» и является безопасным началом. Переходите на -all, когда убедитесь, что все отправители перечислены.
  4. У отдельного домена отправки своя запись SPF. Если Mailgun отправляет с mg.example.com, запись SPF, которую просит Mailgun, ставится на mg.example.com, а не на корневой домен.

Запись DMARC начинается с режима наблюдения:

Запись TXT на _dmarc.example.com
v=DMARC1; p=none; rua=mailto:dmarc@example.com

p=none ничего не меняет в доставке; она лишь просит получателей присылать сводные отчёты, чтобы вы видели, кто отправляет письма от имени вашего домена и проходят ли они проверки. Читайте эти отчёты несколько недель, исправьте всё законное, что не проходит, затем ужесточите политику. Отчёты DMARC и поэтапное внедрение политики проводит через эту последовательность, а ключи DKIM и их ротация рассказывают о размерах ключей и о том, как заменить ключ позже.

Добавьте записи у своего DNS-провайдера, дождитесь распространения и нажмите кнопку проверки в панелях релея и Mailgun. Оба откажутся считать домен подтверждённым, пока записи не разрешаются. DNS-записи простыми словами - введение на случай, если TXT, CNAME и TTL вам незнакомы. Некоторые провайдеры просят для DKIM записи CNAME вместо TXT; это делегирование на их ключ, и так тоже можно, лишь бы имя и значение вы скопировали точно. Многие панели DNS сами дописывают ваш домен, поэтому имя, вставленное целиком, может превратиться в selector._domainkey.example.com.example.com. Смотрите на сохранённую запись, а не на то, что вы набрали.

Проверка и разбор сбоя#

Проверяйте в таком порядке, потому что каждый шаг зависит от предыдущего.

  1. Перезапустите Ghost, чтобы он прочитал новую конфигурацию.
  2. Вызовите настоящее транзакционное письмо. В приватном окне браузера зарегистрируйтесь участником с адресом, который можете прочитать. Ссылка входа или подтверждения должна прийти за считаные секунды. Можно также пригласить сотрудника на свой адрес.
  3. Посмотрите исходные заголовки письма в почтовом клиенте («показать оригинал»). В заголовке Authentication-Results должны быть spf=pass, dkim=pass и dmarc=pass. Если SPF пройден, а DMARC нет, чаще всего дело в выравнивании: релей прошёл SPF для своего домена, а не вашего, и подписи DKIM для вашего домена нет.
  4. Отправьте предпросмотр рассылки и проверьте те же заголовки. У стороны Mailgun свой домен, так что результат может отличаться от шага 3.
  5. Отправьте письма нескольким провайдерам. Ящик у крупного провайдера и у небольшого хостера расходятся в оценке достаточно часто, чтобы лишняя минута окупилась.

Журнал Ghost - первое место, куда стоит смотреть, если ничего не приходит. Ошибки почтового слоя содержат ответ SMTP от релея, который гораздо конкретнее всего, что видно в браузере. Прочитайте строку журнала, прежде чем менять настройки.

Частые ошибки и что они значат#

Ничего не приходит, в журнале нет ошибки. Ghost всё ещё на прямой доставке, либо блок mail не был прочитан. Проверьте, что mail.transport равен SMTP, что его не перекрывает переменная окружения, и перезапустите.

Тайм-аут соединения. Хост или порт релея неверны, либо сеть блокирует порт. Если сеть блокирует исходящий порт 25, это повод использовать 587 или 465 на релей, а не проблема самого Ghost. Порт 25 и блокировки исходящей почты объясняет разницу между портами.

Ошибки TLS или «wrong version number». Порт и secure не согласованы. Для 465 нужно true; для 587 нужно false.

Ошибка аутентификации. Неверны имя пользователя или пароль, либо релею нужен ключ приложения, а не пароль от аккаунта. Некоторые провайдеры используют в качестве имени пользователя слово apikey, а в качестве пароля - сам ключ. Читайте страницу SMTP у релея, а не предполагайте.

Адрес отправителя отклонён. Адрес from или его домен не подтверждены у релея. Подтвердите домен там, затем используйте адрес на нём.

Письмо приходит, но попадает в спам. Записи отсутствуют или не выровнены. Проверьте заголовки, как описано выше. Убедитесь также, что у домена нет двух записей SPF и что DKIM включён в самом релее, а не только опубликован в DNS.

Пункт рассылки недоступен или Mailgun возвращает ошибку при отправке. Ключ API, регион или домен неверны или не заданы. Сначала проверьте регион в baseUrl; несовпадение выглядит как плохой ключ.

Рассылка отправляется, но участники ничего не получают. Mailgun принял пакет; затем доставка не удалась или сработал фильтр. Посмотрите журналы Mailgun по получателю: там видно, доставлено ли письмо, вернулось или отмечено как спам.

Ссылки входа работают для одних адресов и не работают для других. Часто причина в получателе, который отклоняет почту при провале SPF или DKIM, тогда как другие снисходительнее. Исправляйте записи, а не вините почтового провайдера получателя.

Ghost на сервере приложений RE:NODE#

Ghost - одна из линеек с установкой в один клик. Сервер устанавливает и собирает Ghost при первом запуске и хранит данные в SQLite на сервере, так что почта остаётся главным, что нужно устроить. Вы правите config.production.json в файловом менеджере или по SFTP либо задаёте переменные окружения на вкладке Startup и перезапускаете сервер из консоли. Приложения в один клик: WordPress, Ghost и Strapi описывают, что делает установщик.

Для релея есть три честных варианта: провайдер транзакционной почты, собственный SMTP Mailgun или линейка Stalwart Mail Server, если вы хотите сами вести почту. Для случая, когда сеть блокирует исходящий порт 25, у линейки Mail Server есть задокументированный способ: релей задаётся в её веб-админке. Записи MX, SPF, DKIM и DMARC вы в любом случае добавляете сами. Для приёма почты из интернета на таком сервере поддержка должна пробросить порт 25, но Ghost ничего принимать не нужно - он только отправляет.

FAQ#

Можно ли отправлять рассылки Ghost по SMTP вместо Mailgun?

Нет. Путь массовой почты в Ghost построен на API Mailgun. Настройки SMTP несут только транзакционную почту, поэтому релей будет доставлять ссылки входа, но не включит возможность рассылать пост по почте.

Какой порт использовать для SMTP в Ghost, 465 или 587?

Любой, лишь бы secure соответствовал: 465 с "secure": true или 587 с "secure": false. Берите тот, что рекомендует ваш релей. Избегайте порта 25: он предназначен для доставки между серверами и часто заблокирован.

Почему ссылка входа попадает в спам, если SPF пройден?

Потому что DMARC проверяет выравнивание, а не только факт прохождения. Если SPF пройден для домена самого релея, а для вашего ничего не подписано DKIM, домен From не совпадает. Включите DKIM для своего домена в релее и опубликуйте выданный им ключ.

Нужен ли отдельный домен для рассылок?

Нет, но поддомен вроде mg.example.com - обычная схема. Он держит записи DNS Mailgun отдельно от вашей обычной почты и защищает репутацию основного домена, если отправка пойдёт плохо.

Где должен храниться пароль SMTP?

В переменной окружения вроде mail__options__auth__pass, а не в файле, которым вы можете поделиться. В любом случае он читается при запуске, так что перезапустите Ghost после изменения. Смените его, если он хоть раз попал в журнал или скриншот.

Стоит ли поднимать собственный почтовый сервер только ради Ghost?

Обычно нет. Ghost отправляет мало транзакционной почты, а хороший релей даёт репутацию и панель доставки, которые новый сервер зарабатывает неделями. Свою почту стоит вести, если вам нужны ящики под вашим контролем, а не ради нескольких ссылок входа.

Это было полезно?