Pourquoi les e-mails du formulaire de contact n'arrivent pas, et comment y remédier

· Mis à jour le: · · Délivrabilité des e-mails, SPF, DKIM, DMARC, Parcours de contact, B2B

Résumé exécutif

La cause la plus fréquente pour laquelle un e-mail de notification de formulaire de contact « part avec succès mais n’arrive jamais » n’est pas la connexion SMTP en elle-même : c’est que l’expéditeur visible (From:) et l’expéditeur réellement authentifié (le MAIL FROM du SPF / le d= du DKIM) ne correspondent pas. DMARC prend RFC5322.From comme référence et vérifie si SPF ou DKIM — au moins l’un des deux — passe avec alignement. Autrement dit, une conception qui place directement l’adresse Gmail ou professionnelle de l’expéditeur du formulaire dans From: — même si elle semble pratique pour la réponse — est une conception qui échoue facilement en exploitation réelle.

La conclusion pratique est simple. La règle de base est de fixer le From: des e-mails de notification de contact sur le domaine de votre propre site, et de placer l’adresse de l’utilisateur du formulaire dans Reply-To:. N’utilisez Sender: que lorsque « l’auteur et l’entité qui envoie réellement le message diffèrent », et configurez Return-Path non pas en écrivant l’en-tête à la main, mais comme expéditeur d’enveloppe côté MTA ou service de messagerie.

Par ailleurs, le transfert casse facilement SPF, et lorsqu’une liste de diffusion ou un serveur relais réécrit le corps ou les en-têtes, DKIM se casse aussi. C’est précisément pour cela que, pour un formulaire de contact, il est plus sûr de ne jamais dépendre du seul SPF, de toujours activer DKIM, et de commencer DMARC en mode d’observation p=none avant de progresser vers quarantine / reject.

En outre, pour les envois à destination de Gmail, Google exige que le domaine d’envoi configure SPF ou DKIM, que les expéditeurs dépassant un certain volume mettent en place SPF, DKIM et DMARC, et que le domaine de l’en-tête From: corresponde au domaine authentifié par SPF ou par DKIM. La véritable nature des problèmes de délivrabilité ne réside généralement pas dans « le code d’envoi », mais dans « la conception de l’expéditeur ».

Le rôle de SPF, DKIM, DMARC et de l’en-tête From

Dans l’acheminement du courrier, il faut distinguer l’enveloppe et les en-têtes. Ce que SPF valide principalement, c’est le MAIL FROM pendant la session SMTP — l’expéditeur au niveau de l’acheminement. À la livraison finale, ce chemin de retour doit être conservé une seule fois, sous la forme d’un Return-Path, et les systèmes SMTP d’envoi ne devraient pas construire de messages portant déjà un en-tête Return-Path dès le départ. De son côté, From: exprime, côté en-têtes du message, « de qui le courrier semble provenir » ; Reply-To: indique la cible de réponse ; et Sender: indique l’entité qui a réellement transmis le message.

La RFC 5322 définit From: comme l’auteur du message et Sender: comme l’entité d’envoi réelle. Si l’auteur et l’entité d’envoi sont identiques, Sender: ne devrait pas être utilisé ; et si l’on souhaite que la cible de réponse diffère de l’auteur, l’outil correct est Reply-To:. La RFC 5322 précise en outre explicitement que From: ne devrait pas contenir une adresse n’appartenant pas à l’auteur. Une notification de formulaire de contact n’est normalement pas envoyée par l’utilisateur lui-même depuis son MUA — c’est une notification construite par le système du site — donc une conception qui place l’adresse de l’utilisateur dans From: s’écarte aussi du sens de la spécification.

DKIM appose une signature sur une partie des en-têtes et sur le corps du message, que le destinataire vérifie à l’aide de la clé publique publiée dans le DNS. Le domaine signataire apparaît dans le d= de l’en-tête DKIM-Signature, et la clé publique est recherchée via un sélecteur, comme dans selector._domainkey.example.com. DKIM est utilisé comme une authentification qui « survit relativement bien au transfert », mais si le corps ou les en-têtes signés sont modifiés en cours de route, le hachage du corps bh= ou la vérification de la signature échoue.

DMARC ne se contente pas de regarder si SPF et DKIM ont réussi ou non : il examine si les domaines qu’ils ont authentifiés s’alignent avec le domaine de From:. C’est le point crucial. Même si SPF passe, avec MAIL FROM=bounces.vendor.net et From: contact@example.com, l’alignement SPF échoue. Si DKIM passe avec d=example.com, DMARC peut tout de même passer — mais sans DKIM non plus, DMARC échoue. DMARC définit des modes d’alignement strict / relaxed via adkim / aspf, des politiques p=none|quarantine|reject, et des destinataires de rapports rua / ruf.

Les directives récentes de Gmail traduisent directement cette philosophie de conception en exigences opérationnelles. Google indique explicitement de « ne pas usurper l’en-tête From: » et que « pour le courrier direct, le domaine de l’en-tête From: doit correspondre au domaine SPF ou au domaine DKIM ». Une notification de formulaire de contact n’est pas un e-mail marketing, mais la logique de base des filtres côté réception est la même, donc ignorer ce principe fait baisser le taux de délivrabilité.

MX du destinataireDNSMTA d'envoi / service SMTPApplication webUtilisateur du formulaireMX du destinataireDNSMTA d'envoi / service SMTPApplication webUtilisateur du formulaireSoumission du formulaireDétermine From / Reply-To / SenderRequête d'envoi SMTPApplique la signature DKIMMAIL FROM / RCPT TO / DATAConsultation SPF (MAIL FROM)Consultation de la clé publique DKIM (selector._domainkey)Consultation DMARC (_dmarc + domaine From)Évaluation de l'alignementLivré / indésirable / rejeté / bounce

Ce qui compte dans ce flux, c’est que le critère d’évaluation de DMARC reste centré sur le domaine From: jusqu’au bout. Parce que l’application touche From:, le côté SMTP touche MAIL FROM, et le côté DNS touche SPF/DKIM/DMARC — chacun séparément — corriger un seul de ces éléments ne résout pas la non-délivrance.

Scénarios d’échec fréquents pour les formulaires de contact

Le cas le plus fréquent est celui où l’on place l’adresse de l’utilisateur du formulaire dans From:. Par exemple, si l’envoi se fait via le SMTP du site mais que l’on met From: taro@gmail.com, ce qui passe SPF ou DKIM est normalement le domaine du site, pas le domaine Gmail. Il en résulte un décalage : From: affiche Gmail alors que le domaine authentifié est example.com — et l’alignement DMARC échoue. Google lui-même exige d’éviter l’usurpation de From: et la correspondance entre From: et les domaines SPF/DKIM.

Le deuxième cas le plus fréquent est celui où le transfert casse SPF. Lors d’un transfert de courrier, l’IP d’origine vue par le destinataire final tend à devenir « un serveur relais absent du SPF du domaine d’envoi d’origine », si bien que même un e-mail légitime échoue au test SPF. Google indique également que « le courrier transféré échoue facilement au test SPF, il faut donc toujours utiliser DKIM ». De plus, si le relais ajoute un préfixe au sujet, un pied de page, ou touche autrement au corps du message, DKIM se casse également.

Une configuration initiale incomplète lors de l’utilisation d’un service SMTP externe est également typique. Avec SendGrid, il existe des cas où l’envoi est impossible tant que la Domain Authentication n’est pas configurée ; activer Automated Security génère des enregistrements d’authentification basés sur des CNAME, et vous pouvez configurer si besoin un Custom Return Path ou un Custom DKIM Selector. Avec SES, le MAIL FROM utilise par défaut le sous-domaine amazonses.com, de sorte que SPF passe implicitement — mais si vous souhaitez un alignement SPF avec le domaine de votre site, vous devez configurer un MAIL FROM personnalisé. Avec Mailgun également, tant que le SPF/DKIM du domaine d’envoi et les MX requis ne sont pas configurés, une signature d’envoi correcte ne peut pas s’établir.

Le MTA local d’un hébergement mutualisé est un piège facilement négligé. Même si votre site est authentifié, si l’IP qui sort réellement est une IP partagée à mauvaise réputation, sans PTR, ou si l’hébergeur n’implémente pas DKIM, la délivrabilité en pâtit. Google impose le PTR de l’IP d’envoi comme exigence, et indique qu’une mauvaise réputation d’IP partagée peut être à l’origine d’erreurs de la classe 5.7.1.

La manière d’utiliser mail() de PHP ou une bibliothèque d’envoi est un autre point d’échec fréquent. Le manuel PHP explique que mail() requiert un en-tête From, et que le paramètre additionnel permet de spécifier l’expéditeur d’enveloppe via sendmail -f. Autrement dit, la bonne approche est de transmettre l’expéditeur d’enveloppe au MTA, plutôt que de construire soi-même un en-tête Return-Path:. Mal comprendre ce point crée un décalage entre l’expéditeur utilisé pour l’évaluation SPF et celui que l’application suppose.

Enfin, il y a le cas où l’on manipule Sender ou From alors qu’il faudrait utiliser Reply-To. Si l’objectif est seulement de diriger les réponses vers l’utilisateur, Reply-To suffit. Sender est l’en-tête à utiliser lorsqu’on veut expliciter que « l’auteur et l’entité d’envoi réelle diffèrent » — ce n’est pas un en-tête à utiliser couramment dans un formulaire de contact. Distinguer clairement l’objectif de conception — « veut-on que les réponses reviennent à l’utilisateur ? » ou « veut-on indiquer l’entité responsable de l’envoi ? » — réduit les erreurs.

Procédures de diagnostic et commandes

La première chose à faire est de regarder les en-têtes bruts du courrier avant de regarder le code. Dans Gmail, utilisez « Afficher l’original » ; dans Outlook, utilisez « Détails du message » ou « En-têtes Internet » — vous pouvez ainsi vérifier Authentication-Results, Return-Path, From, Reply-To, DKIM-Signature et Received. Cela permet de déterminer, avec une précision considérable, si le problème est « l’envoi ne fonctionne pas » ou « l’authentification et la cohérence se sont rompues ».

Ce qu’il faut vérifier en premier dans les en-têtes

Les cinq priorités absolues sont les suivantes :

  1. Quel est le domaine de From:
  2. Quel est le domaine de Return-Path:
  3. Authentication-Results: affiche-t-il spf=pass / dkim=pass / dmarc=pass
  4. Lorsque dkim=pass, quel domaine apparaît dans header.i= ou d=
  5. Lorsque dmarc=fail, la cause est-elle un échec d’authentification ou un échec d’alignement (alignment failure)

Le guide de dépannage DMARC de Google précise également explicitement que même si un message passe les autres authentifications, DMARC échoue si les en-têtes ne sont pas alignés.

Commandes pour vérifier le DNS

dig est l’outil de référence pour le dépannage DNS ; le manuel de BIND le décrit lui-même comme un outil de consultation DNS offrant une sortie flexible et claire. nslookup est une commande de vérification plus légère, également utilisable en mode non interactif. Pour vérifier l’authentification d’un formulaire de contact, interrogez au minimum ces trois enregistrements : SPF, DKIM et DMARC.

# SPF
dig +short TXT example.com

# DKIM
dig +short TXT form2026._domainkey.example.com

# DMARC
dig +short TXT _dmarc.example.com

# Sous Windows
nslookup -type=txt example.com
nslookup -type=txt _dmarc.example.com
nslookup -type=txt form2026._domainkey.example.com

Voici à quoi ressemble un exemple de sortie attendue :

"v=spf1 include:sendgrid.net include:mailgun.org ip4:203.0.113.10 -all"
"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ..."
"v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com; ruf=mailto:dmarc-afrf@example.com; adkim=r; aspf=r; fo=1"

Ce qu’il faut vérifier ici : l’enregistrement SPF est-il consolidé en un seul, la clé publique DKIM peut-elle être résolue, et DMARC comporte-t-il un p=. La RFC 7208 impose de limiter à 10 au total les mécanismes SPF qui déclenchent des consultations DNS ; au-delà, cela provoque un permerror.

Vérifier la connexion SMTP et le TLS

openssl s_client est le client SSL/TLS polyvalent d’OpenSSL, pratique pour vérifier le STARTTLS et la chaîne de certificats d’un serveur SMTP. -starttls smtp lance le STARTTLS pour SMTP, et -showcerts permet de consulter la liste des certificats renvoyés par le serveur.

printf 'QUIT\r\n' | openssl s_client \
  -connect smtp.example.com:587 \
  -starttls smtp \
  -servername smtp.example.com \
  -showcerts \
  -brief

Exemple de sortie :

CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Verification: OK
250 CHUNKING

Cela permet au moins de vérifier si le serveur SMTP est joignable, si STARTTLS est actif, et s’il n’y a pas d’anomalie manifeste dans la vérification du certificat. Google exige le TLS pour les expéditeurs dépassant un certain volume, et l’absence de TLS peut provoquer une erreur 5.7.29.

Test de reproduction par un envoi réel

swaks est un outil pratique dédié aux tests SMTP, qui permet de reproduire de façon flexible des envois incluant TLS, l’authentification et les extensions SMTP. Pour enquêter sur une non-délivrance de formulaire de contact, il est efficace d’envoyer un seul message « en contournant l’application — même SMTP, même From, même Reply-To, même destinataire » pour reproduire le problème.

swaks \
  --server smtp.example.com \
  --port 587 \
  --tls \
  --auth LOGIN \
  --auth-user contact@example.com \
  --auth-password '********' \
  --from bounce@example.com \
  --to yourtest@gmail.com \
  --h-From "Notifications du site <contact@example.com>" \
  --h-Reply-To "Taro Yamada <visitor@gmail.com>" \
  --header "Subject: swaks test" \
  --body "This is a test"

Un envoi réussi typique ressemble à ceci :

=== Trying smtp.example.com:587...
=== Connected to smtp.example.com.
<-  250-STARTTLS
<-  250-AUTH LOGIN PLAIN
->  STARTTLS
<-  220 Ready to start TLS
...
<-  250 2.0.0 Ok: queued as ABC123DEF

Si cela échoue via l’application mais passe avec swaks, la cause se situe très probablement du côté de la construction des en-têtes par la bibliothèque ou de la configuration de l’expéditeur d’enveloppe. À l’inverse, si swaks échoue de la même façon, on peut restreindre le problème au DNS, au SMTP, ou à la politique côté réception.

Diagnostic externe avec mail-tester

mail-tester est un service qui vous fait envoyer un e-mail vers une adresse de test générée aléatoirement, puis analyse le message, le serveur d’envoi et l’IP d’envoi pour renvoyer un rapport détaillé. Il convient bien comme diagnostic de premier niveau pour les cas où, sur un MTA local ou un serveur mutualisé, le courrier « n’arrive pas, sans raison apparente ».

L’utilisation est simple :

  1. Récupérer l’adresse de test émise par mail-tester
  2. Envoyer un message par le même chemin que le formulaire de contact
  3. Consulter le score et les remarques sur SPF / DKIM / DMARC / la résolution inverse / les listes noires / la composition du corps du message

Il ne faut pas juger la délivrabilité en production sur le seul score de mail-tester, mais cela permet au moins de repérer rapidement des problèmes comme « SPF n’est même pas visible », « la clé publique DKIM ne peut pas être résolue », ou « la composition du corps ou de l’expéditeur est anormale ».

Exemples d’analyse d’en-têtes

Voici un exemple d’échec :

Return-Path: <bounce-123@vendor.example.net>
Authentication-Results: mx.google.com;
       spf=pass (google.com: domain of bounce-123@vendor.example.net designates 198.51.100.10 as permitted sender) smtp.mailfrom=vendor.example.net;
       dkim=none;
       dmarc=fail (p=quarantine sp=quarantine dis=none) header.from=gmail.com
From: Taro Yamada <visitor@gmail.com>
Reply-To: Taro Yamada <visitor@gmail.com>
Subject: Demande de contact

Cet e-mail passe SPF lui-même, mais échoue à DMARC parce que From: est gmail.com. C’est la manière typique dont un formulaire de contact se casse lorsque l’adresse de l’utilisateur est placée dans From:.

Voici un exemple de réussite :

Return-Path: <bounce@example.com>
Authentication-Results: mx.google.com;
       spf=pass smtp.mailfrom=example.com;
       dkim=pass header.i=@example.com header.s=form2026;
       dmarc=pass header.from=example.com
From: Example Site <contact@example.com>
Reply-To: Taro Yamada <visitor@gmail.com>
Subject: Notification de contact

Sous cette forme, la facilité de réponse à l’utilisateur est assurée via Reply-To:, tandis que l’expéditeur visible et l’expéditeur authentifié sont tous deux alignés sur example.com — ce qui stabilise nettement la délivrabilité.

Modèles de conception recommandés pour From

Le principe qui ne varie jamais pour les formulaires de contact est d’aligner « le domaine utilisé pour l’authentification » avec « le domaine From montré au destinataire ». Ensuite, on ne fait sortir que la cible de réponse vers Reply-To:. En répartissant la conception en trois couches — Sender: seulement si nécessaire, Return-Path configuré via l’enveloppe — la conception reste stable.

Modèle Exemple d’en-têtes Cas adapté Avantages Point de vigilance
Modèle recommandé From: contact@example.com
Reply-To: visitor@gmail.com
Return-Path: bounce@example.com
Presque tous les formulaires de contact Facile à faire passer DMARC / facile à répondre / implémentation simple Oublier Reply-To fait que les réponses partent vers le site
Séparation par sous-domaine From: contact@form.example.com
Reply-To: visitor@gmail.com
Return-Path: bounce.form.example.com
Vouloir séparer les notifications de formulaire du courrier principal Séparation de réputation facile / facile à gérer SPF/DKIM/DMARC doivent aussi être mis en place côté sous-domaine
Sender explicite From: contact@example.com
Sender: mailer@example.com
Reply-To: visitor@gmail.com
Exigence particulière de vouloir expliciter l’entité d’envoi Permet de montrer l’entité responsable de l’exploitation Généralement inutile. Redondant si l’auteur et l’entité d’envoi sont identiques
Modèle déconseillé From: visitor@gmail.com
Reply-To: visitor@gmail.com
Implémentation qui veut seulement mettre en avant la cible de réponse Seule l’apparence semble naturelle Cause fréquente d’échec DMARC. À éviter pour les notifications de contact

Les fondements de ce tableau sont la sémantique de From / Sender / Reply-To dans la RFC 5322, le traitement de Return-Path dans la RFC 5321, et la spécification selon laquelle DMARC évalue l’alignement par rapport à From:. Pour la notification de formulaire, le « modèle recommandé » de la première ligne suffit comme choix par défaut. Même dans les cas où l’on est tenté de placer l’adresse de l’utilisateur dans From:, mettre la cible de réponse dans Reply-To: permet d’atteindre l’objectif.

Il faut surtout retenir que Return-Path n’est pas « un en-tête que l’on modifie » mais « le résultat de l’expéditeur d’enveloppe utilisé pour l’acheminement ». Avec PHP mail(), cela passe par -f ; avec un service SMTP, par des paramètres de configuration comme Custom MAIL FROM / Return Path / bounce domain — c’est la bonne façon de l’implémenter.

Guide de configuration par type d’installation

À partir d’ici, nous organisons la façon de penser la configuration pour les trois cas les plus fréquents sur le terrain. Comme prérequis, privilégiez toujours les valeurs exactes générées par la console d’administration de chaque service pour ce que vous entrez dans le DNS. Les exemples d’enregistrements ci-dessous sont représentatifs, destinés à comprendre la structure.

Lorsque le site utilise un service SMTP externe

Le point le plus important avec un SMTP externe est de finaliser d’abord l’authentification de votre propre domaine. Google conseille également, lorsqu’on utilise un fournisseur de service de messagerie, de vérifier que ce service authentifie bien le SPF et le DKIM de votre domaine.

Configuration recommandée

  • From: est contact@example.com ou contact@form.example.com
  • Reply-To: est l’adresse de l’utilisateur du formulaire
  • Return-Path / MAIL FROM est un sous-domaine de bounce que vous gérez vous-même, par exemple bounce.example.com
  • DKIM signe avec example.com ou le sous-domaine d’envoi
  • DMARC se place sur le domaine From: visible

Configuration type pour SendGrid

Avec SendGrid, la Domain Authentication est un prérequis ; activer Automated Security génère trois CNAME. Si elle est désactivée, un MX et deux TXT sont générés, et vous pouvez aussi configurer un Custom Return Path et un Custom DKIM Selector.

; Exemple : SendGrid (utilisez les valeurs générées dans la console d'administration)
em123.example.com.           CNAME   u123456.wl.sendgrid.net.
s1._domainkey.example.com.   CNAME   s1.domainkey.u123456.wl.sendgrid.net.
s2._domainkey.example.com.   CNAME   s2.domainkey.u123456.wl.sendgrid.net.

_dmarc.example.com.          TXT     "v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com"

Le piège classique avec SendGrid est l’état « seule l’authentification SMTP est faite, l’authentification de domaine n’est pas configurée ». Dans cet état, l’envoi fonctionne en soi, mais du point de vue du destinataire, la relation entre From: et le domaine authentifié reste faible. La règle de base est de finaliser la Domain Authentication, puis de configurer un Custom Return Path si nécessaire.

Configuration type pour SES

SES utilise par défaut un MAIL FROM sur le sous-domaine amazonses.com, donc SPF passe implicitement en soi. Mais si vous souhaitez un alignement SPF avec le domaine de votre site, utilisez un MAIL FROM personnalisé. Dans ce cas, SES exige un TXT SPF et un MX sur le domaine du MAIL FROM personnalisé, et le MX doit être exactement unique. De plus, Easy DKIM ajoute trois CNAME au DNS.

; Exemple : SES Easy DKIM
abcde12345._domainkey.example.com.   CNAME   abcde12345.dkim.amazonses.com.
fghij67890._domainkey.example.com.   CNAME   fghij67890.dkim.amazonses.com.
klmno54321._domainkey.example.com.   CNAME   klmno54321.dkim.amazonses.com.

; Exemple : SES custom MAIL FROM
bounce.example.com.                  MX      10 feedback-smtp.ap-northeast-1.amazonses.com.
bounce.example.com.                  TXT     "v=spf1 include:amazonses.com -all"

_dmarc.example.com.                  TXT     "v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com"

Ce qui compte dans la conception SES, c’est de faire du domaine MAIL FROM un sous-domaine dédié aux bounces, et non le domaine From: d’envoi lui-même. AWS conseille également que le MAIL FROM soit un sous-domaine, et non le domaine même depuis lequel le courrier est réellement envoyé.

Configuration type pour Mailgun

Avec Mailgun, la vérification du domaine d’envoi nécessite un TXT pour SPF et un TXT pour DKIM, ainsi que l’ajout de deux MX. Si un SPF existe déjà, n’ajoutez pas un nouvel enregistrement SPF : insérez include:mailgun.org dans l’enregistrement existant. Plusieurs clés DKIM peuvent apparaître, mais l’envoi fonctionne tant que la clé actuellement utilisée est correctement publiée dans le DNS.

; Exemple : utiliser Mailgun sur un sous-domaine
mg.example.com.                      TXT     "v=spf1 include:mailgun.org ~all"
mx._domainkey.mg.example.com.        TXT     "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ..."
mg.example.com.                      MX      10 mxa.mailgun.org.
mg.example.com.                      MX      10 mxb.mailgun.org.
email.mg.example.com.                CNAME   mailgun.org.

_dmarc.example.com.                  TXT     "v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com"

Mailgun s’accorde bien avec une exploitation par sous-domaine ; créer un sous-domaine d’envoi dédié comme mg.example.com facilite la gestion des notifications de formulaire et des notifications transactionnelles.

Lorsque le site envoie via le MTA local d’un hébergement mutualisé

Sur un hébergement mutualisé, la première chose à examiner est la qualité de l’infrastructure d’envoi de l’hébergeur, et non votre propre application. Si le PTR de l’IP d’envoi, la prise en charge de DKIM, la réputation de l’IP partagée, ou la visibilité des journaux d’envoi sont faibles, cette configuration est déjà désavantagée de ce seul fait. Google accorde lui aussi de l’importance au PTR de l’IP d’envoi, et indique qu’une mauvaise réputation d’IP partagée peut être une cause de blocage.

En pratique, il est plus sûr de procéder dans cet ordre :

  1. Vérifier si l’hébergeur permet de configurer SPF/DKIM/PTR via la console d’administration ou le support
  2. Toujours fixer From: sur votre propre domaine
  3. Inclure dans SPF l’IP d’envoi de l’hébergeur ou le domaine d’envoi autorisé
  4. Activer DKIM via la fonctionnalité de l’hébergeur. À défaut, basculer vers un SMTP externe
  5. Si possible, séparer l’adresse de bounce du MAIL FROM, comme bounce.example.com

Comme exemple typique de création de DKIM par vous-même sur un hébergement mutualisé, la famille OpenDKIM permet de générer la clé privée et l’enregistrement TXT pour le DNS avec opendkim-genkey. La structure consistant à placer la clé publique DKIM sur selector._domainkey.example.com avec un sélecteur correspond à la RFC 6376.

mkdir -p /etc/opendkim/keys/example.com
opendkim-genkey -D /etc/opendkim/keys/example.com -d example.com -s form2026
chown opendkim:opendkim /etc/opendkim/keys/example.com/form2026.private
chmod 600 /etc/opendkim/keys/example.com/form2026.private

Voici à quoi ressemble le résultat après la génération :

form2026._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ..."
# /etc/opendkim/KeyTable
form2026._domainkey.example.com example.com:form2026:/etc/opendkim/keys/example.com/form2026.private

# /etc/opendkim/SigningTable
*@example.com form2026._domainkey.example.com

Cela dit, si vous ne pouvez pas contrôler vous-même le PTR ou le relais sortant sur un hébergement mutualisé, migrer vers un SMTP externe est le raccourci le plus sûr. Même pour un envoi à faible volume comme les notifications de formulaire de contact, un MTA local faiblement authentifié est désavantagé face à Gmail et au courrier professionnel.

Lorsque le site utilise PHP mail() ou une bibliothèque SMTP

PHP mail() est pratique, mais du point de vue de l’authentification et de la délivrabilité, tout dépend de « quel est le MTA en aval ». Le manuel PHP explique que le courrier nécessite un en-tête From, et que pour un envoi via sendmail_path, le paramètre additionnel permet de spécifier l’expéditeur d’enveloppe. Autrement dit, utiliser mail() ne met pas automatiquement en ordre SPF/DKIM/DMARC.

Voici, a minima, comment concevoir cela :

  • From: est contact@example.com
  • Reply-To: est l’utilisateur du formulaire
  • L’expéditeur d’enveloppe est bounce@example.com
  • Indiquer également l’adresse de l’utilisateur dans le corps du message
  • Si possible, utiliser un SMTP authentifié plutôt que mail()

Exemple minimal avec mail()

Insérer directement une saisie utilisateur dans les en-têtes est dangereux. Si des caractères CR/LF se glissent dans $name ou $email, un attaquant peut injecter des en-têtes supplémentaires comme Bcc:, transformant le formulaire en relais de spam. Le manuel PHP recommande également de toujours valider/normaliser les entrées externes utilisées dans les en-têtes. Dans l’exemple ci-dessous, toutes les valeurs injectées dans les en-têtes sont assainies au préalable, y compris l’argument de l’expéditeur d’enveloppe (additional_params).

<?php

// Ne renvoie que des valeurs utilisables dans un en-tête. Rejette toute valeur contenant CR/LF/NUL.
function sanitize_header_value(string $value): string {
    if (preg_match('/[\r\n\0]/', $value)) {
        throw new InvalidArgumentException('Invalid characters in header value');
    }
    return trim($value);
}

// Valide l'adresse e-mail conformément à la RFC.
function sanitize_email(string $email): string {
    $clean = sanitize_header_value($email);
    if (!filter_var($clean, FILTER_VALIDATE_EMAIL)) {
        throw new InvalidArgumentException('Invalid email address');
    }
    return $clean;
}

$to = 'ops@example.com';
$subject = sanitize_header_value('Notification de contact');

// $name / $email / $message sont les entrées du formulaire. $message va dans le corps, donc CR/LF y est toléré,
// mais $name / $email, utilisés dans les en-têtes, doivent toujours rejeter CR/LF.
$safeName  = sanitize_header_value($name);
$safeEmail = sanitize_email($email);

$body = <<<TEXT
Name: {$safeName}
Email: {$safeEmail}

{$message}
TEXT;

$headers = [
    'From' => 'Example Site <contact@example.com>',
    'Reply-To' => sprintf('%s <%s>', $safeName, $safeEmail),
    'Content-Type' => 'text/plain; charset=UTF-8',
];

// additional_params est aussi transmis au shell ; n'utilisez que des valeurs fixes, sans mélanger d'entrée dynamique.
mail($to, $subject, $body, $headers, '-fbounce@example.com');

Cet exemple comporte deux points clés. D’une part, Return-Path: n’est pas écrit comme en-tête — l’expéditeur d’enveloppe est transmis via -f dans le cinquième argument. D’autre part, la saisie utilisateur injectée dans des en-têtes comme Reply-To: passe par une fonction de désinfection qui rejette CR/LF. Si l’on construit les en-têtes sans désinfection, un attaquant peut injecter une chaîne telle que \r\nBcc: victim@example.com pour insérer des en-têtes supplémentaires — c’est pourquoi le manuel PHP considère la validation des entrées externes utilisées dans les en-têtes comme obligatoire. Comme additional_params finit lui aussi par être transmis au shell, ne l’alimentez qu’avec des valeurs fixes, sans y mélanger de saisie utilisateur.

Exemple avec une bibliothèque SMTP

<?php
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->Port = 587;
$mail->SMTPAuth = true;
$mail->SMTPSecure = 'tls';
$mail->Username = getenv('SMTP_USER');
$mail->Password = getenv('SMTP_PASS');

$mail->setFrom('contact@example.com', 'Example Site');
$mail->addAddress('ops@example.com');
$mail->addReplyTo($email, $name);

// Selon la bibliothèque, Sender / return-path peuvent être configurés séparément
$mail->Sender = 'bounce@example.com';

$mail->Subject = 'Notification de contact';
$mail->Body = $body;

$mail->send();

L’avantage d’une bibliothèque SMTP est qu’elle facilite le contrôle séparé de l’expéditeur d’en-tête et de l’expéditeur d’enveloppe. Pour un formulaire de contact, elle convient le mieux à la conception qui fixe From: sur le domaine du site et ne place que la cible de réponse dans Reply-To:.

Exemples concrets pour SPF, DKIM et DMARC

Exemple de base pour SPF

example.com. TXT "v=spf1 ip4:203.0.113.10 include:sendgrid.net -all"

Avec SPF, il faut inclure toutes les sources qui envoient réellement du courrier. Lorsque vous utilisez un expéditeur tiers, Google demande également de vérifier que cet expéditeur est bien authentifié par SPF et DKIM. Notez aussi que SPF a une limite sur le nombre de consultations DNS, il faut donc faire attention à ne pas empiler trop d’include.

Exemple de base pour DKIM

form2026._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ..."

Les sélecteurs servent à la rotation des clés et permettent de faire coexister plusieurs clés publiques sur le même domaine. En exploitation, plutôt que default, un nom qui se distingue par l’usage ou par la date facilite la relecture ultérieure.

Exemples de déploiement de DMARC

D’abord, le mode d’observation :

_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com; ruf=mailto:dmarc-afrf@example.com; adkim=r; aspf=r; fo=1"

Ensuite, lorsque l’on veut mettre en quarantaine une partie du trafic :

_dmarc.example.com. TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc-agg@example.com; adkim=r; aspf=r"

Enfin, l’exploitation stricte :

_dmarc.example.com. TXT "v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc-agg@example.com; adkim=s; aspf=s"

p=none signifie surveillance uniquement, quarantine recommande la mise en quarantaine, et reject recommande le rejet pendant la session SMTP. adkim et aspf basculent entre strict et relaxed. Plutôt que de passer directement à reject, il est plus sûr d’observer le volume et les sources légitimes avec none, puis de monter progressivement.

Par ailleurs, lorsque rua / ruf sont envoyés vers un service d’agrégation externe à l’entreprise, la RFC 7489 exige un enregistrement DNS supplémentaire côté tiers. Par exemple, pour envoyer les rapports de example.com vers thirdparty.example.net, le destinataire doit publier example.com._report._dmarc.thirdparty.example.net TXT "v=DMARC1".

Liste de vérification pour le dépannage

Pour finir, voici un ordre de vérification directement utilisable sur le terrain. Pour une non-délivrance d’e-mails de formulaire de contact, traiter les points dans l’ordre, du haut vers le bas, est le chemin le plus rapide.

À vérifier en premier

  • From: est-il sur votre propre domaine
  • L’adresse de l’utilisateur est-elle bien dans Reply-To:
  • Authentication-Results affiche-t-il spf=pass ou dkim=pass, et en plus dmarc=pass
  • Si dmarc=fail, s’agit-il d’un échec d’authentification ou d’un échec d’alignement
  • SPF est-il consolidé en un seul enregistrement
  • Le nombre de consultations SPF n’est-il pas excessif
  • La clé publique DKIM peut-elle être résolue
  • DMARC comporte-t-il un p=
  • Le PTR et la résolution inverse de l’IP d’envoi sont-ils cohérents
  • Utilisez-vous une IP partagée, et si oui, sa réputation s’est-elle dégradée

Où regarder dans les bounces et les journaux

Si des e-mails de bounce arrivent, les champs importants dans le DSN au format message/delivery-status sont Final-Recipient, Status, Action et Diagnostic-Code. La RFC 3464 définit ces informations d’échec de livraison lisibles par machine. Par exemple, une ligne comme Diagnostic-Code: smtp; 550 relay not permitted indique un rejet au niveau SMTP, et non au niveau de l’application.

Côté serveur, il faut examiner au moins les journaux d’acheminement du MTA. Avec Postfix, on y voit les succès et échecs de livraison, l’accumulation dans la file d’attente, les refus de relais, les échecs de résolution DNS, et les avertissements du milter DKIM. Il en va de même avec Exim. Voici quelques commandes de vérification représentatives.

# Exemple : Postfix
journalctl -u postfix -n 200 --no-pager
postqueue -p

# Exemple : Exim
exim -bp

Selon l’hébergement, le chemin des journaux et les droits d’exécution des commandes varient ; vérifiez donc d’abord si vous pouvez consulter « les journaux d’acheminement du courrier », et non « les journaux de l’application ». Dans un environnement où cela n’est pas visible, migrer vers un SMTP externe facilite l’analyse des problèmes.

Comment consulter les en-têtes dans Gmail et Outlook

Dans Gmail, « Afficher l’original » ; dans Microsoft Outlook, « Détails du message » ou « En-têtes Internet » permettent de consulter les en-têtes bruts. Pour enquêter sur une non-délivrance de formulaire de contact, il est plus efficace de sauvegarder et comparer le texte intégral des en-têtes plutôt que des captures d’écran.

Comment interpréter les erreurs côté réception

Les erreurs de la famille Gmail permettent de lire facilement la cause à partir du code.

Exemple d’erreur Signification Remède principal
5.7.27 Échec SPF Ajouter la source à l’enregistrement SPF
5.7.30 Échec DKIM Corriger la clé DKIM / la configuration de signature
4.7.32 Incohérence entre le domaine organisationnel de From: et de SPF/DKIM Revoir la conception de From:
5.7.25 Défaut de PTR / résolution inverse Corriger la résolution inverse de l’IP d’envoi

La FAQ de Google précise elle aussi ces erreurs et les orientations de résolution correspondantes. Pour les formulaires de contact, le cas le plus fréquent est 4.7.32le défaut d’alignement.

Le critère de jugement final

Si les trois conditions suivantes sont réunies simultanément, la conception de la notification du formulaire de contact peut être considérée comme solide.

  1. From: est sous example.com
  2. Reply-To: est l’adresse de l’utilisateur du formulaire
  3. Authentication-Results affiche dmarc=pass

Si ces trois éléments sont réunis, la conception tient la route, que vous utilisiez SendGrid, SES, Mailgun, un hébergement mutualisé ou une bibliothèque SMTP. À l’inverse, si l’un d’eux manque, soupçonner en premier la conception de From: est le chemin le plus court.

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Création de sites web

Parce que la conception du formulaire de contact, la gestion de l'expéditeur des e-mails de notification et la conception du Reply-To — c'est-à-dire l'ensemble du parcours de contact — sont des sujets qui s'articulent naturellement avec la création de site web.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Quelle est la cause la plus fréquente de non-réception des e-mails du formulaire de contact ?
Ce n'est pas la connexion SMTP elle-même, mais le fait que l'expéditeur visible (From:) et l'expéditeur réellement authentifié (le MAIL FROM du SPF / le d= du DKIM) ne correspondent pas. Le cas le plus fréquent est une conception qui place directement l'adresse Gmail de l'utilisateur du formulaire dans From:. Si l'envoi se fait via le SMTP du site mais que From: taro@gmail.com, l'authentification porte sur le domaine du site alors que From: affiche Gmail — ce décalage fait échouer l'alignement DMARC.
Comment faut-il configurer le From des e-mails de notification du formulaire de contact ?
La règle de base est de fixer From: sur le domaine de votre propre site et de placer l'adresse de l'utilisateur du formulaire dans Reply-To:. Cela préserve la facilité de réponse tout en alignant, sur le même domaine, l'expéditeur visible et l'expéditeur authentifié, ce qui stabilise le taux de délivrabilité. N'utilisez Sender: que lorsque l'auteur et l'entité qui envoie réellement le message diffèrent, et configurez Return-Path non pas en écrivant l'en-tête à la main, mais comme expéditeur d'enveloppe côté MTA ou service de messagerie.
Par quoi commencer l'enquête sur une non-délivrance d'e-mails ?
Avant même de regarder le code, examinez les en-têtes bruts de l'e-mail reçu. Dans Gmail, utilisez « Afficher l'original » pour vérifier Authentication-Results, Return-Path, From et DKIM-Signature. La priorité absolue est de vérifier le domaine de From:, le domaine de Return-Path:, puis si spf / dkim / dmarc passent (pass). En cas de dmarc=fail, déterminez s'il s'agit d'un échec d'authentification ou d'un échec d'alignement (alignment failure). Côté DNS, utilisez dig ou nslookup pour interroger les trois enregistrements SPF, DKIM et DMARC.
Peut-on configurer DMARC directement sur reject ?
Plutôt que de passer directement à reject, il est plus sûr de commencer en mode d'observation p=none pour vérifier le volume et les sources d'envoi légitimes, puis de monter progressivement vers quarantine, puis reject. p=none signifie surveillance uniquement, quarantine recommande la mise en quarantaine, et reject recommande le rejet pendant la session SMTP. Par ailleurs, comme le transfert casse facilement SPF et que la réécriture du corps ou des en-têtes lors d'un relais casse aussi DKIM, il est important de ne jamais dépendre du seul SPF et de toujours activer DKIM.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog