Короткий ответ

У домена contoso.example должен быть ровно один TXT, который начинается с v=spf1. Для почты в Microsoft 365 в нём обязателен include:spf.protection.outlook.com. Политика на конце — -all, когда инвентарь отправителей закрыт, не +all. Вложенные include/a/mx/redirect суммарно не должны требовать больше 10 DNS-запросов (RFC 7208). «Too many DNS lookups» = permerror = для DMARC это не pass, письма ведут себя как fail.

Не плодите второй SPF «для Mailchimp». Не вставляйте mx «на всякий случай», если MX и так Exchange Online: это лишние lookups. Сначала нарисуйте список отправителей, потом одну строку.

Симптомы и как отличить

  • nslookup -type=TXT contoso.example показывает два v=spf1.
  • В Authentication-Results: spf=permerror или spf=fail (fail — хост не в механизме; permerror — запись сломана).
  • После добавления include:thirdparty.example обычная почта Outlook стала попадать под permerror.
  • Письма с contoso.onmicrosoft.com проходят, с кастомного домена — нет: смотрят не ту зону.
НаблюдениеДиагнозНе то
Два SPF TXTневалидный SPF как таковой«нужен SPF на поддомене»
permerror lookups>10 вложенных запросов«Microsoft сломал DNS»
fail, один include EOписьмо шло не из EOсломанный DKIM
pass на return-path другого доменаSPF не выровнен к From«SPF ок, DMARC обязан пройти»

Возможные причины

  1. Хостер при подключении Microsoft 365 добавил свой SPF, не заменив старый.
  2. Несколько include маркетинга, CRM, старого регистратора, плюс mx, плюс a — взрыв lookups.
  3. +all или ?all после миграции «чтобы точно доходило».
  4. Нет include:spf.protection.outlook.com, исходящие из Exchange Online fail.
  5. SPF повешен на www или на mail, а люди шлют с apex contoso.example.
  6. Макрос %{i} и экзотика, скопированная с форума, плюс ptr (дорого и непредсказуемо; ptr в SPF лучше не использовать).
  7. TXT разрезан так, что у регистратора получилось два рекорда вместо одного длинного.

Диагностика

1. Сколько SPF

nslookup -type=TXT contoso.example
Resolve-DnsName contoso.example -Type TXT |
  Where-Object { $_.Strings -join '' -like 'v=spf1*' }

Больше одной строки v=spf1 — стоп. Сначала слияние, потом lookups.

Проверяйте именно apex, с которого From. Поддомен info.contoso.example имеет свой SPF, если с него шлют.

2. Ручной разбор lookups

Считайте: каждый include, a, mx, ptr, exists, redirect — как минимум один запрос, плюс то, что внутри include. ip4:/ip6: lookups не тратят. include:spf.protection.outlook.com — несколько вложенных, это нормально и уложимо, если не добавить ещё пять ESP.

Цепочка:

nslookup -type=TXT spf.protection.outlook.com

Не копируйте в свой SPF содержимое Microsoft: используйте include, чтобы они могли менять диапазоны.

3. Совпадает ли источник письма с SPF

Заголовок Received / spf=pass (sender IP is …). IP должен попадать в механизмы. Если IP офиса 203.0.113.40, а в SPF только include Microsoft — сканер, отправивший напрямую, получит fail. Это не «SPF настроен неправильно для Outlook», это неучтённый отправитель.

4. Связка с DMARC

Даже идеальный SPF не спасёт, если Return-Path bounce.crm.example, а From ivan.petrov@contoso.example, без DKIM на вашем домене. Тогда идите в DKIM и проверку комплекта.

Решение

Целевой минимум для только Exchange Online

Один TXT на contoso.example:

v=spf1 include:spf.protection.outlook.com -all

TTL поставьте умеренный (например 600–3600) на время отладки, потом можно поднять. Не делайте 60 секунд навсегда.

Слияние двух записей

Было: v=spf1 mx a -all у хостера и отдельно v=spf1 include:spf.protection.outlook.com ~all. Станет одна, без лишнего mx, если почтовый контур — Microsoft 365. a оставляйте, только если веб-сервер на том же имени реально шлёт почту — обычно нет.

Добавить ESP, не превысив 10

Предпочитайте include поставщика, который сам укладывается в 1–2 lookup, или ip4: их фиксированных шлюзов, если документация даёт стабильные сети. Если после добавления permerror — уберите mx/a/include мёртвого регистратора. Не решайте permerror через +all.

Политика -all vs ~all

~all (softfail) терпим на период инвентаря. Боевой контур Microsoft 365 + закрытый список отправителей — -all. +all не используйте. ?all почти ничего не даёт фильтрам.

Поддомены

Сканер с From: scanner@contoso.example должен попасть в SPF apex. Либо шлите через Exchange, либо ip4: шлюза, либо отдельный scan.contoso.example со своим SPF и From на этом поддомене (плюс DMARC sp).

Как проверить, что проблема устранена

  1. Ровно один v=spf1 на apex.
  2. Письмо из Outlook / OWA: spf=pass, IP в диапазоне Microsoft.
  3. Письмо с каждого ESP: pass или вы сознательно перевели их на DKIM-only alignment.
  4. Нет permerror на контрольных заголовках через 2×TTL.
Resolve-DnsName contoso.example -Type TXT
Connect-ExchangeOnline -UserPrincipalName admin@contoso.example
Get-MessageTrace -SenderAddress ivan.petrov@contoso.example `
  -StartDate (Get-Date).AddHours(-2) -EndDate (Get-Date)

Трейс не показывает SPF сам по себе — смотрите заголовки копии на контрольном ящике.

Если не помогло

  • Pass в заголовках, DMARC fail: проблема alignment/DKIM, не «ещё один include».
  • Fail только у одного получателя: они могли закешировать старый SPF (их право); подождите TTL, не плодите записи.
  • Используете flattening (разворот include в ip4): документ устаревает, когда Microsoft меняет диапазоны. Для EO flatten обычно хуже, чем include.
  • Зона на split-DNS: внутренний TXT без include, внешний другой. Проверяйте публичный резолвер, не DC.

Профилактика

  • Чеклист отправителей в wiki: Exchange Online, ESP, шлюз, сканеры.
  • Любой новый SaaS с From вашего домена — изменение SPF или DKIM, не «потом».
  • После миграции на Microsoft 365 удалите include старого ISP.
  • Не копируйте SPF конкурента. Готовьте домен по чеклисту.

FAQ

Нужен ли mx в SPF для Exchange Online?

Обычно нет. MX указывает, куда принимать, SPF — кто шлёт. Исходящие EO покрывает include:spf.protection.outlook.com. mx тратит lookups.

Можно ли несколько SPF, если регистратор так рисует GUI?

Нет. Несколько TXT v=spf1 — ошибка. Один TXT, в нём несколько механизмов.

include чужого ESP «на будущее»?

Нет. Лишний include = lookups и право ESP слать от вашего имени с точки зрения SPF.

Почему письмо с contoso.onmicrosoft.com проходит без вашего SPF?

Это другой домен. SPF смотрит зону envelope sender. Кастомный From без своей записи — другая история (DKIM/DMARC).

SPF pass, но Gmail в спаме. SPF бесполезен?

SPF — один сигнал. Репутация, DKIM, контент, жалобы. Чините спам как класс, не добавляйте +all.

Как считать lookups, если include Microsoft «внутри ещё include»?

Считаются вложенные запросы до листа. Документация Microsoft рассчитана на укладку в лимит; ваша задача — не добавить сверху пять тяжёлых include.