# SPF (Sender Policy Framework)

> Référence RFC 7208 : syntaxe de l'enregistrement (mécanismes, qualificateurs, modificateurs, macros), algorithme d'évaluation check_host(), limites de requêtes DNS, codes de résultat et pièges courants.

Source: emailmarketing.net — https://emailmarketing.net/fr/apprendre/authentification/spf

SPF vous permet de publier, dans le DNS, les hôtes autorisés à envoyer des emails avec votre domaine. Les serveurs de réception comparent l'adresse IP du client qui se connecte à cette liste. SPF vérifie l'identité **RFC5321.MailFrom** (l'expéditeur d'enveloppe, « Envelope From », ou `Return-Path`) et, séparément, l'identité donnée dans **HELO ou EHLO**.

SPF est défini par la RFC 7208. Il authentifie le chemin suivi par le message, au moyen d'un enregistrement DNS TXT publié par le domaine. Pour savoir comment SPF alimente l'alignement DMARC, voir [DMARC](https://emailmarketing.net/fr/apprendre/authentification/dmarc).

## Identités vérifiées

- Les vérificateurs **DOIVENT** vérifier l'identité `MAIL FROM` si aucune vérification HELO n'a été effectuée ou si elle n'a pas abouti à un résultat définitif.
- Il est **RECOMMANDÉ** que les vérificateurs vérifient aussi, séparément, l'identité `HELO`. Lorsque `MAIL FROM` est vide (rebonds, `MAIL FROM:<>`), le domaine HELO sert de domaine MAIL FROM (`postmaster@<HELO domain>`).

## L'enregistrement

- L'enregistrement est publié sous forme d'enregistrement DNS **TXT** sur le domaine vérifié. Il **doit commencer exactement** par `v=spf1` (par exemple, `v=spf10` ne correspond pas et est ignoré). La RFC 7208 a rendu obsolète le type d'enregistrement DNS propre à SPF (type 99) : publiez donc uniquement du TXT.
- S'il n'existe **aucun** enregistrement `v=spf1`, le résultat est **none**. S'il en existe **plus d'un**, le résultat est **permerror**.
- Les termes sont évalués **de gauche à droite**, et le **premier mécanisme qui correspond** décide du résultat, selon son qualificateur. Si rien ne correspond et qu'aucun `redirect=` n'est présent, le résultat par défaut est **neutral** (un `?all` implicite).

## Qualificateurs

| Qualificateur | Résultat quand le mécanisme correspond |
|---|---|
| `+` (par défaut s'il est omis) | pass |
| `-` | fail |
| `~` | softfail |
| `?` | neutral |

## Mécanismes

| Mécanisme | Syntaxe | Signification | Requête DNS ? |
|---|---|---|---|
| `all` | `all` | Correspond toujours. Placé en dernière position comme valeur par défaut explicite (`-all`, `~all`, …). Tout ce qui suit `all` est ignoré. | Non |
| `include` | `include:<domain>` | Évalue récursivement l'enregistrement SPF de `<domain>`. Si le résultat récursif est **pass**, le mécanisme correspond. S'il est **fail**, **softfail** ou **neutral**, le mécanisme ne correspond pas et l'évaluation continue. **temperror** et **permerror** sont transmis vers le haut. Un **none** récursif donne **permerror**. | Oui |
| `a` | `a[:<domain>][/<cidr4>][//<cidr6>]` | Correspond si l'adresse IP du client est égale à un enregistrement A (IPv4) ou AAAA (IPv6) du domaine cible (par défaut, le domaine courant), éventuellement dans un préfixe CIDR. | Oui |
| `mx` | `mx[:<domain>][/<cidr4>][//<cidr6>]` | Recherche les enregistrements MX de la cible, puis les enregistrements d'adresse de chaque nom MX, et correspond si l'adresse IP du client en fait partie. | Oui |
| `ptr` | `ptr[:<domain>]` | Une vérification par DNS inverse validé. **« Ce mécanisme NE DEVRAIT PAS être publié »** : il est lent, peu fiable et obsolète dans la pratique. | Oui |
| `ip4` | `ip4:<network>[/<cidr>]` | L'adresse IP du client est comprise dans le réseau IPv4. Le préfixe par défaut est `/32`. | Non |
| `ip6` | `ip6:<network>[/<cidr>]` | L'adresse IP du client est comprise dans le réseau IPv6. Le préfixe par défaut est `/128`. | Non |
| `exists` | `exists:<domain-spec>` | Développe les macros du domaine et effectue une requête **A** (même pour les connexions IPv6). Correspond si au moins un enregistrement A est renvoyé. Cela permet une politique pour chaque adresse IP grâce aux macros. | Oui |

Longueurs de préfixe CIDR : `/0`–`/32` pour IPv4 (par défaut `/32`), et `/0`–`/128` pour IPv6 (par défaut `/128`). Seul le nombre indiqué de bits de poids fort est comparé.

## Modificateurs

Les modificateurs sont des paires associant un nom et une valeur (nom=valeur). Chacun peut apparaître au plus une fois, n'importe où dans l'enregistrement.

| Modificateur | Syntaxe | Signification |
|---|---|---|
| `redirect` | `redirect=<domain>` | Si **aucun mécanisme n'a correspondu**, l'évaluation se poursuit avec l'enregistrement SPF de `<domain>`, dont le résultat est repris tel quel. Si la cible de la redirection n'a pas d'enregistrement SPF, le résultat est **permerror** (et non none). Il compte dans la limite de 10 requêtes. Il est ignoré lorsque l'enregistrement contient aussi `all` (puisque `all` correspond toujours en premier). |
| `exp` | `exp=<domain>` | En cas de résultat **fail**, un enregistrement TXT est demandé pour ce domaine, ses macros sont développées, et il est renvoyé comme explication destinée à être lue par des personnes. Il ne compte pas dans la limite de 10 requêtes. |

## Macros

Les macros (`%{x}`) peuvent être développées dans les champs `domain-spec` et dans le texte de `exp` :

| Macro | Se développe en |
|---|---|
| `%{s}` | Expéditeur (l'adresse MAIL FROM complète) |
| `%{l}` | Partie locale de l'adresse de l'expéditeur |
| `%{o}` | Domaine de l'expéditeur |
| `%{d}` | Domaine en cours de vérification |
| `%{i}` | Adresse IP du client (notation décimale pointée pour IPv4 ; quartets séparés par des points pour IPv6) |
| `%{p}` | Domaine DNS inverse validé de l'adresse IP du client (NE DEVRAIT PAS être utilisé, pour les mêmes raisons que `ptr`) |
| `%{v}` | `in-addr` pour IPv4, `ip6` pour IPv6 |
| `%{h}` | Le domaine donné dans HELO ou EHLO |
| `%%` / `%_` / `%-` | `%` littéral / espace / espace encodée pour URL (`%20`) |

Transformateurs : un chiffre limite le nombre de libellés conservés, en comptant à partir de la droite (`%{d2}` conserve les deux derniers libellés), `r` inverse l'ordre des libellés, et d'autres caractères délimiteurs peuvent suivre.

## Codes de résultat

| Résultat | Signification (RFC 7208 §2.6) | Traitement recommandé par le serveur de réception (§8) |
|---|---|---|
| **none** | Aucun domaine valide n'a été extrait, ou aucun enregistrement SPF n'a été trouvé | Aucune information ; non concluant |
| **neutral** | Le domaine n'affirme explicitement rien au sujet de l'adresse IP (`?`) | **DOIT** être traité exactement comme `none` |
| **pass** | Le client est autorisé à envoyer des emails pour le domaine | Le domaine est responsable ; poursuivre |
| **fail** | Le client n'est explicitement **pas** autorisé (`-`) | Politique locale. En cas de rejet, utiliser SMTP **550** avec le code d'état étendu **5.7.1** |
| **softfail** | L'hôte n'est probablement pas autorisé (`~`), et le domaine est en transition | **NE DEVRAIT PAS** rejeter sur ce seul motif ; **PEUT** examiner de plus près |
| **temperror** | Une erreur temporaire (généralement DNS) pendant l'évaluation | Accepter ou reporter. En cas de report, utiliser SMTP **451** avec le code d'état **4.4.3** |
| **permerror** | L'enregistrement publié n'a pas pu être interprété correctement (l'opérateur DNS doit le corriger) | En cas de rejet, utiliser SMTP **550** avec le code d'état **5.5.2** |

Remarque pour DMARC : `fail`, `softfail`, `neutral`, `none` et les deux erreurs comptent tous comme « non pass ». Seul **pass** (avec alignement) peut satisfaire le volet SPF de DMARC.

## Limites de requêtes DNS (§4.6.4)

| Limite | Valeur | En cas de dépassement |
|---|---|---|
| Termes qui interrogent le DNS dans une évaluation (`include`, `a`, `mx`, `ptr`, `exists`, `redirect`) | **10 au total**, comptés sur toute la récursion à travers `include` et `redirect` | **permerror** |
| « Requêtes vides » (*void lookups* : NXDOMAIN ou réponse vide) | DEVRAIENT être limitées à **2** | **permerror** |
| Requêtes d'adresse pour chaque mécanisme `mx` | **10** noms MX | **permerror** |
| Requêtes d'adresse pour chaque évaluation `ptr` | **10** noms PTR | Les enregistrements au-delà des 10 premiers sont **ignorés** |

`all`, `ip4`, `ip6` et `exp` ne comptent **pas** dans la limite de 10 termes.

## Pièges courants

- **Plus de 10 requêtes.** Les chaînes d'`include` imbriquées des ESP, des CRM et des outils de gestion de tickets s'additionnent vite. Le résultat est permerror, que DMARC traite comme une absence totale de SPF. Aplatissez les includes ou retirez les prestataires que vous n'utilisez plus, et préférez `ip4` ou `ip6` (qui ne coûtent aucune requête) à `a` ou `mx` quand c'est possible.
- **Plusieurs enregistrements `v=spf1`** sur un même nom donnent permerror. Fusionnez-les en un seul enregistrement.
- **`+all` (ou une valeur par défaut absente ou permissive)** autorise l'Internet entier, ce qui est pire que de n'avoir aucun enregistrement. Terminez les enregistrements par `-all` ou `~all`.
- **Un `include` d'un domaine sans enregistrement SPF** donne permerror (un none récursif). Vérifiez les includes de vos prestataires lorsque vous cessez d'utiliser un service.
- **Le mécanisme `ptr` et la macro `%{p}`** sont obsolètes, lents et peu fiables. Ne les publiez pas.
- **Le transfert casse SPF.** L'adresse IP du service de transfert ne figure pas dans l'enregistrement du domaine d'origine, si bien que SPF échoue après tout saut qui conserve le MAIL FROM d'origine. C'est inhérent à l'authentification fondée sur le chemin. C'est pourquoi DKIM, qui authentifie le contenu, est préféré pour réussir DMARC après un transfert, et c'est la raison d'être d'[ARC](https://emailmarketing.net/fr/apprendre/authentification/arc).
- **Un pass SPF n'est pas un pass DMARC.** De nombreux ESP utilisent leur propre domaine de rebond dans le MAIL FROM : SPF réussit donc, mais n'est pas aligné avec le domaine From:. Pour l'alignement, utilisez comme Return-Path (adresse de rebond) un sous-domaine personnalisé du domaine From:. Voir [DMARC](https://emailmarketing.net/fr/apprendre/authentification/dmarc).
- **Les chaînes TXT de plus de 255 caractères** doivent être découpées en plusieurs chaînes entre guillemets au sein de l'enregistrement unique (elles sont concaténées). Les gros enregistrements peuvent aussi imposer le DNS sur TCP.
- **Un `redirect=` placé après `all`** ne prend jamais effet, car `all` correspond en premier.

## Voir aussi

- [DKIM](https://emailmarketing.net/fr/apprendre/authentification/dkim), qui authentifie plutôt le contenu
- [DMARC](https://emailmarketing.net/fr/apprendre/authentification/dmarc), sur la façon dont les résultats SPF et l'alignement alimentent la politique
- [ARC](https://emailmarketing.net/fr/apprendre/authentification/arc), sur la conservation des résultats d'authentification à travers les transferts
