← Retour aux Insights

Centre de préférences et RGPD : quand les antivirus désabonnent vos clients

Pierre Frin Septembre 2026 7 min de lecture
un seul lien dans le pied de page Me désabonner CloudPage passerelle de sécurité GET automatique clic humain POST du formulaire désabonné choix daté Marketing Cloud Engagement · CloudPages

Un réseau d'agences de voyage envoie sa newsletter professionnelle un mardi matin. Dans l'heure qui suit, le taux de désabonnement dépasse plusieurs pour cent, alors qu'il tourne d'habitude autour de quelques dixièmes. Les départs sont concentrés sur une poignée de domaines d'entreprises, et ils arrivent tous dans les minutes qui suivent l'envoi.

Personne n'a cliqué. Les passerelles de sécurité de ces entreprises ouvrent chaque lien du message pour l'analyser, y compris celui du pied de page libellé « Me désabonner en un clic ». Ce lien ouvrait une CloudPage qui désabonnait l'abonné dès son chargement, puis affichait une confirmation. Des clients se retrouvent désabonnés sans le savoir, et s'étonnent quelques semaines plus tard de ne plus rien recevoir.

La même semaine, une cliente conteste avoir accepté les offres partenaires. L'équipe ne peut montrer qu'une case cochée dans une data extension, réécrite à chaque passage sur la page. Pas de date d'origine, pas de trace du formulaire, pas de texte présenté à l'époque. Les deux incidents ont la même racine : une page de préférences conçue comme un écran d'administration, pas comme un point de collecte.

Un clic n'est pas une preuve de volonté

La documentation de Salesforce sur les ouvertures et les clics est explicite : les analyses antivirus parcourent le message et suivent les liens, et toute activité, même déclenchée par un système, est attribuée à l'abonné. Sur un envoi grand public, cela gonfle un taux de clic. Sur une page de désabonnement, cela produit des départs que personne n'a demandés, et le phénomène frappe surtout les adresses professionnelles.

Écrire seulement en POST

La règle qui en découle tient en une phrase : une requête GET ne modifie jamais un statut ni une préférence. La page ouverte depuis l'email affiche un choix, et seul l'envoi du formulaire par un bouton, en POST, déclenche l'écriture. Les robots qui suivent les liens ne soumettent pas les formulaires.

GET  : affichage des choix, création d'un jeton à usage unique
POST : contrôle du jeton, écriture des préférences, désabonnement

List-Unsubscribe, la sortie la plus sûre

L'en-tête List-Unsubscribe échappe au problème. Salesforce l'ajoute aux envois commerciaux, il accepte le désabonnement en un clic décrit par la RFC 8058, et le client de messagerie envoie alors un POST que Salesforce traite lui-même. Vous ne pouvez ni le désactiver ni le modifier, et c'est une bonne nouvelle : des deux chemins de sortie proposés au destinataire, c'est le plus sûr.

⚠️ Le signal à surveiller : un pic de désabonnements concentré sur quelques domaines professionnels, dans les minutes qui suivent un envoi. Ce n'est pas un rejet de votre contenu, c'est une passerelle de sécurité qui fait son travail.

Ce que la page doit laisser comme trace

L'article 7 du RGPD pose deux exigences quand un traitement repose sur le consentement : le responsable du traitement doit être en mesure de démontrer que la personne l'a donné, et le retrait doit être aussi simple que le recueil. Pour la prospection par email, la CNIL rappelle que le consentement préalable est la règle vis-à-vis des particuliers, avec une exception pour les clients sollicités sur des produits ou services analogues, et demande un moyen simple de s'opposer dans chaque message ainsi qu'une liste d'opposition tenue à jour.

Ces textes sont publics, leur lecture appartient à votre délégué à la protection des données, et cet article ne remplace pas son avis. Ce qui nous revient, c'est de traduire l'exigence de démonstration en champs. Une case à true dans une table de préférences dit ce que le client a choisi en dernier. Elle ne dit ni quand, ni où, ni sur quel texte.

Élément de preuveChampExemple
La personneClientId100245
La finalité et le choixFinalite, Valeurpartenaires, true
Le momentDateEvenementDate et heure du serveur, fuseau documenté
Le point de collecteSourcecentre_preferences, creation_compte, magasin
Le texte présentéVersionTextePART-2026-03, qui renvoie à la table des libellés
Le contexte d'envoiJobIdIdentifiant de l'envoi qui a amené le client sur la page

Un historique en insertion seule

Cet historique s'écrit en insertion seule, avec une ligne par finalité à chaque soumission, même quand la valeur ne change pas. C'est la confirmation qui fait trace, pas seulement le changement. La table des préférences reste à côté, comme vue de l'état courant, pratique pour les requêtes de ciblage.

Versionner le texte présenté

Le libellé affiché mérite le même soin. Lisez-le dans une table de textes versionnés à partir de la version en cours, et transmettez cette version au traitement du formulaire plutôt que de la coder en dur dans la page qui écrit. Le jour où le texte change, une version figée dans le traitement continue d'enregistrer l'ancienne référence, sans la moindre erreur, et la trace devient fausse.

💡 Le fuseau : Now() renvoie l'heure du serveur. Documentez ce fuseau dans la description de la data extension, ou convertissez la valeur avant de l'écrire. Une trace dont on ignore le fuseau perd beaucoup de son intérêt.

Quatre portées de désabonnement

Un refus n'a pas la même portée selon la façon dont il est enregistré, et c'est la source d'un malentendu fréquent entre le libellé promis au client et l'effet obtenu.

MécanismePortéeRemarque
Préférence dans une data extensionAucune tant que les envois ne la lisent pasÀ appliquer par une exclusion ou une requête de ciblage
Désabonnement d'une liste de publicationLes envois associés à cette listeC'est l'action du centre d'abonnement standard
En-tête List-UnsubscribeComme le centre d'abonnement, au niveau de la listeTraité par Salesforce, en POST, ni désactivable ni modifiable
LogUnsubEvent sans contexte d'envoiDésabonnement global de l'abonnéVisible dans All Subscribers
LogUnsubEvent avec le contexte de l'envoiLa liste de l'envoi, l'événement est rattaché au jobLe désabonnement apparaît dans le suivi de l'envoi
Désabonnement de business unitUne business unit d'un compte Enterprise 2.0Visible dans _BusinessUnitUnsubscribes depuis l'entreprise parente

Une case libellée « ne plus recevoir aucun email commercial » engage la portée la plus large. Testez les deux variantes sur un compte de recette, avec et sans contexte d'envoi, et vérifiez ce que devient le statut dans All Subscribers avant de figer le libellé.

Appeler LogUnsubEvent au bon moment

LogUnsubEvent est une requête Execute de l'API SOAP, appelée depuis l'AMPscript avec CreateObject et InvokeExecute. Elle a besoin d'au moins un identifiant d'abonné : SubscriberKey, SubscriberID ou EmailAddress. Si plusieurs sont fournis, ils doivent désigner le même abonné. Le contexte d'envoi passe par JobID, ListID et BatchID, et le JobID suffit puisque la plateforme retrouve les deux autres. Sans contexte exploitable, le désabonnement devient global. Le paramètre Reason est libre.

%%[
/* page de traitement, atteinte en POST, jeton déjà consommé */
SET @lue = CreateObject("ExecuteRequest")
SetObjectProperty(@lue, "Name", "LogUnsubEvent")

SET @prop = CreateObject("APIProperty")
SetObjectProperty(@prop, "Name", "SubscriberKey")
SetObjectProperty(@prop, "Value", @clientId)
AddObjectArrayItem(@lue, "Parameters", @prop)

SET @prop = CreateObject("APIProperty")
SetObjectProperty(@prop, "Name", "Reason")
SetObjectProperty(@prop, "Value", "Centre de preferences, refus total")
AddObjectArrayItem(@lue, "Parameters", @prop)

SET @code = InvokeExecute(@lue, @statut, @requestId)
]%%

Journaliser les codes de retour

Journalisez le résultat, sans quoi un échec passe inaperçu. La documentation retient le statut Event posted comme un succès, et considère les codes 12012 et 401 comme le signe d'un abonné déjà désabonné. Un test qui ignore ces deux codes remplit le journal de faux échecs, et un journal plein de faux échecs n'est plus lu par personne.

Une dernière précaution sur le contexte d'envoi. Les chaînes de personnalisation qui le portent devraient être renseignées sur une page ouverte depuis un lien CloudPagesURL, mais pas sur un envoi de test. Vérifiez leur contenu sur votre compte avant de vous appuyer dessus, parce qu'un contexte vide ne produit pas d'erreur, il change seulement la portée du désabonnement.

LogUnsubEvent ne sait que désabonner. Une réinscription, après un nouveau consentement explicite, passe par une mise à jour du statut de l'abonné et mérite une procédure à part, validée avec votre délégué à la protection des données.

Vérifier que le refus est appliqué

Une préférence enregistrée ne sert à rien si les envois l'ignorent. Une requête quotidienne compare les refus totaux à ce que dit All Subscribers, et écrit dans une table de contrôle en mode Overwrite.

SELECT
    p.ClientId,
    s.Status,
    p.DateMaj
FROM Preferences AS p
INNER JOIN _Subscribers AS s
    ON s.SubscriberKey = p.ClientId
WHERE p.OptinNewsletter  = 'false'
  AND p.OptinPartenaires = 'false'
  AND s.Status = 'active'
  AND p.DateMaj < DATEADD(HOUR, -1, GETDATE())

Le délai d'une heure évite de signaler les refus en cours de traitement. La forme de la comparaison dépend du type réel de vos champs de préférence, alors testez-la dans une Query Activity avant de mettre la requête en production. Toute ligne qui reste est un refus non appliqué, qu'il s'agisse d'un appel en échec, d'une portée différente de celle attendue, ou d'un client qui a décoché ses cases sans demander de refus total. Ce dernier cas n'est pas une anomalie, mais vos requêtes de ciblage doivent l'exclure. Depuis l'entreprise parente, la même logique s'applique aux désabonnements par business unit.

Si vous venez d'Adobe Campaign

Dans Adobe Campaign, le refus global correspond au champ de liste de blocage du destinataire, anciennement appelé liste noire, et les choix par finalité aux abonnements à des services d'information, dont la plateforme conserve l'historique.

Marketing Cloud couvre le refus global avec le statut dans All Subscribers et les listes de publication. L'historique des consentements par finalité, lui, n'existe pas en standard : c'est à vous de le construire dans une data extension, avec la date, la source et la version du texte. C'est la principale charge de conception d'un centre de préférences sur mesure, et celle que l'on découvre le jour où il faut répondre à une réclamation.

Le réflexe à garder

Avant d'ouvrir le code de votre centre de préférences, posez une seule question : que se passe-t-il si un robot charge cette page sans rien cliquer ? Si la réponse contient une écriture, vous avez un incident en attente, et une recette humaine ne le reproduira jamais. Le reste, la trace datée, les versions de texte, la portée du désabonnement, se construit ensuite.

Questions fréquentes

Pourquoi les désabonnements augmentent-ils juste après un envoi sans que personne ne clique ?

Les passerelles de sécurité de certaines entreprises ouvrent chaque lien du message pour l'analyser, y compris le lien de désabonnement du pied de page. La documentation de Salesforce précise que toute activité, même déclenchée par un système, est attribuée à l'abonné. Si la page de désabonnement écrit dès son chargement, ces analyses produisent des départs que personne n'a demandés. Le signal typique est un pic concentré sur quelques domaines professionnels, dans les minutes qui suivent l'envoi.

Comment empêcher un robot de désabonner un contact depuis une CloudPage ?

La règle est qu'une requête GET ne modifie jamais un statut ni une préférence. La page ouverte depuis l'email se contente d'afficher un choix et de créer un jeton à usage unique, et seul l'envoi du formulaire par un bouton, en POST, déclenche le contrôle du jeton puis l'écriture des préférences. Les robots qui suivent les liens ne soumettent pas les formulaires.

Quels champs conserver pour démontrer un consentement dans Marketing Cloud ?

Une case à true dans une table de préférences dit ce que le client a choisi en dernier, mais ni quand, ni où, ni sur quel texte. L'article traduit l'exigence de démonstration en champs : la personne (ClientId), la finalité et le choix, le moment (DateEvenement), le point de collecte (Source), le texte présenté (VersionTexte) et le contexte d'envoi (JobId). Cet historique s'écrit en insertion seule, avec une ligne par finalité à chaque soumission, même quand la valeur ne change pas, la table des préférences restant à côté comme vue de l'état courant.

Que signifient les codes 12012 et 401 renvoyés par LogUnsubEvent ?

La documentation retient le statut Event posted comme un succès, et considère les codes 12012 et 401 comme le signe d'un abonné déjà désabonné. Il faut donc journaliser le résultat de l'appel, sans quoi un échec passe inaperçu. Un test qui ignore ces deux codes remplit le journal de faux échecs, et un journal plein de faux échecs n'est plus lu par personne.

Une préférence stockée dans une data extension suffit-elle à arrêter les envois ?

Non : une préférence dans une data extension n'a aucune portée tant que les envois ne la lisent pas, elle doit être appliquée par une exclusion ou une requête de ciblage. Le désabonnement d'une liste de publication porte sur les envois associés à cette liste, et l'en-tête List-Unsubscribe agit au même niveau. Pour vérifier que le refus est bien appliqué, une requête quotidienne compare les refus totaux à ce que dit All Subscribers et écrit dans une table de contrôle en mode Overwrite.

Dans la même série
Pierre Frin
Fondateur Grokium · Consultant CRM · Certifié Adobe Campaign Classic et Salesforce Marketing Cloud Email Specialist

Votre centre de préférences tient-il face à une réclamation ?

Je peux relire vos pages de préférences, vos appels de désabonnement et vos traces de consentement, et vous rendre un diagnostic écrit avec les correctifs à appliquer.

Me décrire votre contexte →