SPF-запись — это одна DNS TXT запись, которая начинается с v=spf1 и содержит разделённые пробелами термины: механизмы с опциональными квалификаторами и модификаторы. Синтаксис описан в RFC 7208, и любая ошибка в записи инвалидирует её целиком: получатель проверяет синтаксис до начала оценки и сразу возвращает PermError.
Механизмов восемь. all всегда совпадает, поэтому его ставят в конец как явный дефолт, всё после него игнорируется. include:domain — это не склейка, а условный переход: он совпадает, только если запись включённого домена вернула Pass, а Fail или Softfail внутри просто означают «нет совпадения, продолжаем». a и mx проверяют IP по A/AAAA записям домена или его MX-хостам, поддерживают CIDR-суффиксы. ip4 и ip6 проверяют вхождение в конкретную сеть и не тратят DNS-запросы. exists:domain делает один A-запрос и совпадает при любом ответе — это основа динамической авторизации. ptr RFC прямо называет «do not use»: он медленный и нагружает .arpa-серверы.
Квалификаторы +, -, ~, ? задают результат при совпадении, по умолчанию +. На практике важен последний термин: -all (жёсткий Fail) или ~all (Softfail). Google советует ~all, Microsoft — -all при наличии DKIM и DMARC.
Модификаторы — это пары name=value, только с =. redirect=domain передаёт оценку другой записи, но только если ни один механизм не совпал, и игнорируется при наличии all. exp=domain задаёт TXT-запись с объяснением при Fail.
Макросы %{...} подставляют свойства сообщения. Всего их одиннадцать: s, l, o, d, i, p, h, c, r, t, v. Например, %{i} — это IP клиента, а трансформеры вроде %{ir} переворачивают части. Макросы с s, l, o, h ломают кэширование, их лучше избегать.
Оценка идёт слева направо, первое совпадение завершает обработку — порядок механизмов это семантика. Если ничего не совпало и нет redirect, результат Neutral.
DNS-лимиты: не больше 10 запросов на оценку, считается include, a, mx, ptr, exists и redirect. Отдельный лимит — два «пустых» запроса (NXDOMAIN или ноль ответов), превышение даёт PermError. ip4, ip6, all и exp не считаются.
Записей должно быть ровно одна на домен, только тип TXT. Старый тип 99 из экспериментальной фазы отброшен. Лимит 255 байт относится к одной строке внутри TXT, а не ко всей записи: можно разбить на несколько строк в одной записи, они склеятся без пробелов. Весь DNS-ответ лучше держать в пределах 450 октетов для UDP.
Примеры: Google использует v=spf1 include:_spf.google.com ~all. Для своего сервера добавляют ip4 и mx. Salesforce в проде использует макрос: v=spf1 exists:%{i}._spf.mta.salesforce.com -all — один запрос авторизует любой IP. Синтаксические ошибки редки: по исследованию IMC 2023, настоящие ошибки синтаксиса лишь у 0.5% доменов. Чаще путают = и :, ставят лишние пробелы или точки, превышают лимит в 10 запросов. Проверить свою запись можно через SPF checker, а мониторить почту — через DMARCguard.