Il s'agit d'une histoire d'il y a quelque temps : pour répondre aux exigences du RGPD et de la directive ePrivacy, nous avons développé en interne un CMP (outil de gestion du consentement) léger, dédié à notre usage. En général, on opte pour un SaaS, mais nous souhaitons expliquer pourquoi nous l'avons développé et comment nous nous y sommes pris.
Commençons par le consentement aux cookies : pour les sites qui proposent leurs services à l'international, il est devenu incontournable. Le RGPD européen en est l'exemple phare, mais aux États-Unis, le nombre de lois sur la vie privée propres à chaque État augmente d'année en année. Mesurer l'audience ou activer des balises publicitaires sans recueillir le consentement n'est donc plus une pratique courante.
Dans l'UE, le RGPD et la directive ePrivacy interdisent de déposer des cookies non essentiels, comme GA4 ou Google Ads, ou de transmettre des données de navigation à des tiers avant d'avoir obtenu le consentement. Considérer la fermeture de la bannière comme un consentement est tout aussi interdit, tout comme rendre le retrait du consentement plus difficile que son octroi.
Cette réglementation s'applique aussi aux entreprises situées hors de l'UE, dès lors qu'elles proposent des services à des personnes résidant dans l'UE ou suivent leur comportement. Un site japonais qui propose une version anglaise et diffuse ses services à l'international, ou qui utilise le remarketing, peut donc être concerné, même sans établissement dans l'UE.
Google a également ajouté ses propres exigences. Pour utiliser les fonctionnalités de Google Ads auprès des visiteurs de l'UE, la prise en charge de Consent Mode v2 est obligatoire. Si l'état du consentement n'est pas transmis correctement à Google, la mesure et le remarketing ne fonctionnent plus. Au Japon, la révision de 2023 de la loi sur les télécommunications a également instauré la « réglementation des transmissions externes », qui impose de publier la liste des informations envoyées à chaque service.
Il faut donc un dispositif qui satisfasse simultanément les trois conditions suivantes.
- Déterminer la région du visiteur et, dans l'UE notamment, ne déclencher aucune balise tant que le consentement n'a pas été donné
- Transmettre correctement l'état du consentement à Google Consent Mode et proposer un moyen simple de le retirer à tout moment
- Publier la liste des destinataires externes et des cookies (politique) et la maintenir en cohérence avec les transmissions réelles

Pourquoi ne pas avoir utilisé un outil de gestion du consentement du commerce ?
Il y a trois raisons.
- Conflits entre balises.
Nous voulions éviter qu'un script externe de gestion du consentement n'entre en conflit avec GTM, les balises publicitaires et la navigation SPA, au point de faire disparaître même les conversions des utilisateurs ayant donné leur consentement - Les balises à contrôler sont fixes.
Les outils de mesure et de publicité que nous utilisons relèvent presque tous de Google (GTM / GA4 / Google Ads). Les balises à gérer étant connues d'avance, la complexité d'un outil générique n'est pas nécessaire - Cela correspond à nos points forts.
Le développement frontend et l'accessibilité sont notre domaine de prédilection. Construire nous-mêmes un dispositif léger et auditable nous permet de ne fournir que les fonctions nécessaires
Principe de base : notification au Japon, refus par défaut à l'étranger
Voici les règles que nous avons définies pour chaque région.
Visiteur | État initial | Écran | Balise Google |
|---|---|---|---|
Depuis le Japon | Autoriser | Pas de bannière | Démarrage normal |
Depuis un pays autre que le Japon | Refuser | Bannière en bas de l'écran (Tout autoriser / Tout refuser / Paramètres détaillés). Après le choix, un bouton de paramètres flottant | Démarrage uniquement après consentement |
Indéterminé | Refuser | Identique à l'étranger | Démarrage uniquement après consentement |
Signal GPC détecté | Refus des publicités uniquement | Afficher les raisons du refus des publicités | Utilisation publicitaire suspendue |
Au Japon, le principe est d'informer et de publier les informations conformément à la règle de « transmission externe » ; aucune bannière n'est affichée. À l'étranger, l'état initial est fixé au refus pour l'Union européenne comme pour les États-Unis. De nombreuses lois d'États américains reposent principalement sur le retrait (opt-out), cette approche peut donc paraître stricte, mais nous l'avons uniformisée du côté de la sécurité afin de simplifier l'exploitation.
Refuser lorsque l'évaluation est impossible relève de la même logique. En cas de doute, on privilégie de ne pas activer les balises. C'est le principe de conception qui traverse l'ensemble du projet, et le comportement en cas d'échec de chargement, abordé plus loin, suit également cette orientation.
Retirer son consentement doit être aussi simple que le donner
Le RGPD exige que le retrait du consentement soit aussi simple que son octroi. Il n'est pas équilibré de pouvoir accepter en deux clics depuis la bannière, tandis que le retrait n'est possible qu'au fond de la page de politique de confidentialité.
Le bouton « Paramètres des cookies », qui ouvre la boîte de dialogue de paramétrage, est placé dans la section « Modifier ou retirer votre consentement » de la page de politique, quelle que soit la région. En mode étranger, un bouton de paramétrage flottant est en outre affiché en permanence en bas à gauche de l'écran après le choix dans la bannière, afin de pouvoir modifier ou retirer son consentement depuis n'importe quelle page. En mode Japon, aucune bannière n'étant affichée, le bouton flottant n'apparaît pas et seul le bouton de la page de politique est disponible. Selon les projets, il est possible de basculer vers « afficher aussi au Japon » ou « ne pas afficher ».
En cas de retrait, les cookies Google référencés dans le registre seront supprimés et l'envoi de données sera interrompu.
Composition : 3 éléments
[Edge] Cloudflare Worker が request.cf.country を見て
<html data-lcmp-region="jp|overseas"> を HTML に注入
[HTML] <head> 先頭の同期スクリプト(loader.js)が
地域・保存済みの選択・GPC から gtag('consent','default') を確定
→ 日本 or 有効な同意あり → GTM を読む
→ それ以外 → GTM を読まず、バナー UI を遅延読み込み
[UI] 選択 → 検証・保存 → gtag('consent','update') → GTM を読み込む(何度呼ばれても挿入は1回だけ)
[GTM] 各タグ側の「追加の同意チェック」で最終ガード
- Détection de la région — détermination du pays à l'Edge et marquage du HTML
- Script de consentement — à partir du marquage et des choix enregistrés, décide de l'affichage de la bannière et du déclenchement de GTM
- Registre — liste des services externes et des cookies utilisés. Données source de la politique et des suppressions lors d'un retrait
① La détection de la région marque le HTML à l'Edge
La détection du pays peut se faire simplement avec request.cf.country de Cloudflare, mais le point déterminant de la conception était l'endroit où l'utiliser.
Faire interroger une API de détection par le navigateur (la forme privilégiée par de nombreux grands outils étrangers) présente un inconvénient : tant que la réponse n'est pas revenue, la région reste inconnue, et les visiteurs japonais se retrouvent eux aussi brièvement dans l'état « refus ». Nous avons donc choisi de réécrire le HTML à l'Edge pour y intégrer l'attribut de région <html>. Comme il suffit d'ajouter data-lcmp-region="jp" via HTMLRewriter, le script peut lire la région de manière synchrone.
// dist/_worker.js/index.js(サイト側の post-build で生成)
import inner from './astro-entry.js';
import { wrapWorker } from './lg-region-wrap.js';
export default wrapWorker(inner);
Le site de notre société repose sur Astro + @astrojs/cloudflare, ce qui a posé quelques difficultés. Avec un adaptateur qui génère _worker.js, le répertoire functions/ est ignoré, et les pages pré-rendues ne passent pas non plus par le middleware d'Astro. Au final, nous injectons le code en enveloppant avec un wrapper le point d'entrée du Worker généré après le build.
Pas de détection par la langue
Nous avons envisagé de le déterminer via <html lang>document.cookie ou navigator.language, mais si une personne résidant dans l'UE ouvrait une page en japonais et était identifiée à tort comme résidant au Japon, le tag se déclencherait sans consentement, ce qui constituerait une erreur à risque.
Pour les projets où il n'est pas possible d'intercaler une détermination côté serveur (par exemple sur AWS ou un serveur mutualisé, lorsque le DNS ne peut pas pointer vers Cloudflare), nous proposons une méthode qui interroge notre point de terminaison de détermination par adresse IP. Il s'agit du même principe que celui des outils de gestion du consentement courants, à savoir une API de détermination fournie par le prestataire. Le service utilise l'adresse uniquement pour la détermination : ni l'IP ni le code pays ne sont conservés, et la réponse se limite à deux valeurs, jp ou overseas. Dernier recours si cette méthode n'est pas utilisable : un mode qui estime le pays à partir du fuseau horaire de l'appareil (si le fuseau est Asia/Tokyo, le pays est considéré comme le Japon).
La méthode de détermination est choisie pour chaque site dans les paramètres. Quel que soit ce réglage, le script examine d'abord les informations disponibles de manière synchrone.
- Attribut de région intégré au HTML (prioritaire si une détermination Edge est en place)
- Région inscrite dans un cookie (dans une configuration où le HTML ne peut pas être modifié et où le serveur transmet le résultat de la détermination via un cookie)
- Détermination par la méthode choisie dans les paramètres (API de détermination par IP ou fuseau horaire), uniquement si ni 1 ni 2 ne sont disponibles
- Si aucune valeur ne peut être obtenue, le visiteur est traité comme étant hors du Japon** (**par précaution)
Si l'attribut et le cookie sont toujours prioritaires, c'est pour pouvoir passer plus tard à une détermination Edge en ne modifiant que les paramètres. Si l'API de détermination par IP expire, le visiteur est simplement traité comme étant hors du Japon, et les balises ne se déclenchent jamais sans consentement.
② Avant le consentement, on ne « lit pas » plutôt qu'on « bloque »
Le Consent Mode de Google propose deux modes : Advanced, qui charge GTM en amont et envoie uniquement un signal de « refus », et Basic, qui ne charge pas le GTM principal tant que le consentement n'a pas été donné. Nous avons choisi Basic. Si seul le script de consentement est chargé avant le consentement, la structure devient beaucoup plus simple à gérer.
- Même si le chargement du script échoue, les balises non essentielles ne se déclenchent pas, car personne ne lit GTM.
- Le snippet GTM n'est pas écrit directement dans le HTML. Nous n'optons pas non plus pour une distribution sous forme de balise HTML personnalisée GTM (car GTM serait alors lu avant le consentement).
- Côté GTM, chaque balise est également configurée avec la case « ne se déclenche pas sans consentement à l'analyse/publicité », afin de protéger le dispositif à double niveau.
Le fait qu'« avant le consentement, 0 requête et 0 cookie » est vérifié au niveau réseau par des tests E2E.
const FORBIDDEN_BEFORE_CONSENT = [
'**googletagmanager.com/**', '**google-analytics.com/**',
'**googleadservices.com/**', '**doubleclick.net/**', // …
];
test('T02: overseas first visit sends nothing', async ({ page }) => {
const hits: string[] = [];
await page.route('**/*', route => {
if (FORBIDDEN_BEFORE_CONSENT.some(p => matchGlob(p, route.request().url()))) hits.push(route.request().url());
return route.continue();
});
await page.goto('/');
await expect(page.getByRole('region', { name: /cookie/i })).toBeVisible();
expect(hits).toEqual([]); // 通信 0 件
expect(await page.context().cookies()).toEqual([]); // Cookie 0 件
});
Lors du retrait du consentement, seuls les cookies connus répertoriés dans le registre sont supprimés. Nous ne procédons pas à une suppression par caractère générique. Comme il s'agit de « supprimer ce que l'on connaît », si le Domain / Path du registre ne correspond pas à la réalité, la suppression échoue. Faire échouer les tests lorsque le registre est incorrect est ce qui permet de garantir durablement la cohérence entre le tableau de la politique et les communications réelles.
③ Intégrations hors GTM (YouTube, cartes, polices Web)
Arrêter GTM ne suffisait pas. Les iframes YouTube et Google Maps, ainsi que les polices Web non auto-hébergeables, se chargent directement depuis la page sans passer par GTM, et déposent des cookies avant le consentement.
Nous avons opté pour une méthode consistant à réécrire un attribut à un seul endroit dans le HTML.
<iframe data-lcmp-src="<https://www.youtube-nocookie.com/embed/ID>" data-lcmp-service="youtube" width="560" height="315"></iframe>
Avant le consentement, un espace réservé généré à partir des valeurs du registre s'affiche (« Ce contenu est chargé depuis YouTube, et votre adresse IP ainsi que des informations de navigation sont transmises au fournisseur » + boutons « Afficher cette fois uniquement / Toujours afficher »), puis le contenu réel est chargé après autorisation.
Nous n'avons pas adopté le « blocage automatique », qui surveille le DOM et réécrit les éléments avant leur chargement. Il entraîne inévitablement des chargements manqués et des défauts d'affichage, et ce type de fonctionnalité, même issu de produits étrangers, n'est pas garanti. Réécrire un attribut d'iframe à un seul endroit demande un peu plus d'effort, mais fonctionne de manière fiable.
Les polices web peuvent être bloquées selon le même principe. Il suffit de remplacer <link rel="stylesheet">l'attribut href par data-lcmp-href : avant le consentement, le CSS des polices n'est pas lu, et il est chargé après autorisation. Le loader s'exécute de manière <head>synchrone et traite immédiatement les éléments ajoutés. Ainsi, lorsque l'autorisation est accordée, l'attribut <link>href est restauré juste après que l'analyseur a inséré la balise link, et le moment de chargement des polices reste pratiquement identique à celui de l'implémentation classique.
<link rel="stylesheet" data-lcmp-href="<https://fonts.googleapis.com/css2?family=…>" data-lcmp-service="google-fonts">
Toutefois, avec un système fondé sur le consentement, les polices ne sont pas chargées lors de la première visite depuis l'étranger, et le site s'affiche avec les polices système. Si cela pose problème, l'auto-hébergement des fichiers de police sur votre serveur est la solution adéquate : il supprime tout envoi vers des services externes, et la question disparaît donc entièrement.
Il suffit d'ajouter une ligne, pour 8 Ko.
Prérequis : les balises de mesure et de publicité doivent être gérées via GTM. Les sites qui n'utilisent pas GTM ne peuvent pas être intégrés tels quels ; il faut d'abord migrer les balises vers GTM.
Côté intégration, il suffit de placer un script synchrone en <head>tête de page.
<script src="<https://cdn.liberogic.jp/lcmp/><siteId>/loader.js"></script>
La taille de loader.js est d'environ 8 Ko compressé en gzip. L'interface des bannières et des boîtes de dialogue est dans un fragment séparé, chargé à la demande uniquement lorsque nécessaire.
Par ailleurs, l'insertion de gtm.js de GTM est configurée avec l'option « après LCP + quelques secondes ». Avec ce réglage, les visiteurs qui quittent le site avant l'insertion ne sont pas mesurés ; c'est un compromis, mais il limite l'impact sur le score Lighthouse.
Publication par site et interface d'administration
Les paramètres (registre, ID GTM, textes et thème) sont intégrés dans le bundle au moment de la publication, puis distribués via le CDN. Aucune configuration JSON n'est récupérée à l'exécution. Ce choix permet de concilier « initialisation synchrone sans communication réseau » et « interdiction d'injecter des paramètres arbitraires depuis l'extérieur ». Toute modification de configuration implique donc une nouvelle publication et un nouveau déploiement. Chaque publication est conservée en version, de sorte qu'en cas de problème, il est possible de revenir à la version précédente.
Les livrables comprennent loader.js et ui.js, ainsi qu'un extrait de code prêt à coller, un guide d'intégration et un modèle de conteneur GTM préconfiguré avec la vérification du consentement (fichier JSON à importer). L'objectif est de réduire le risque qu'une balise sans vérification du consentement subsiste dans le GTM du site cible.
Le tableau de bord d'administration permet de publier sans avoir de compétences de développement. Voici ses principales fonctions.
- Séparation entre brouillon et publication.
Le registre constitue une déclaration juridique concernant les transmissions de données vers l'extérieur de chaque site ; il n'est donc pas appliqué immédiatement après modification. Avant publication, un récapitulatif des modifications en japonais présente les différences avec les paramètres actuellement publiés. Si le registre a été modifié sans mise à jour de la date de révision de la politique, un avertissement est affiché. Il est possible de revenir à la version précédente en une seule opération depuis l'historique des publications. - Catalogue des services courants.
Les services fréquemment utilisés sur les sites japonais, tels que GA4, Google Ads, Clarity, YouTube et reCAPTCHA, peuvent être ajoutés au registre avec leur nom, leur fournisseur, leur finalité et les informations types relatives aux cookies. Les catégories sont proposées par défaut, la décision finale revenant à une personne. - Analyse automatique des cookies.
Les pages cibles sont ouvertes dans un navigateur réel selon deux états : « avant consentement » et « tout autoriser ». Les cookies déposés et les destinations des communications sont comparés au registre. Les éléments déclenchés avant consentement sont signalés comme « critiques », et ceux absents du registre comme « attention ». Chaque constat peut être ajouté au registre en un seul clic. - Rendu automatique du tableau des politiques.
Il suffit d'insérer sur la page de politique. Le tableau est généré par loader.js à partir du registre intégré, de sorte que la politique publiée ne peut pas diverger des contrôles réellement appliqués. - Édition du thème.
Les couleurs des bannières, boîtes de dialogue et boutons flottants peuvent être modifiées en visualisant l'aperçu réel. Le contraste est calculé en direct : 4,5:1 pour le texte et 3:1 pour les éléments non textuels tels que les bordures de boutons. Un avertissement est affiché si le seuil n'est pas atteint.
Conclusion
La gestion du consentement ne se limite pas à « afficher une bannière ». Elle ne fonctionne réellement que lorsque quatre éléments s'articulent : détection de la zone géographique, contrôle des balises, publication de la politique et parcours de retrait du consentement. Ici, tout cela a été regroupé dans un seul script externe et un seul registre. Modifier le registre met simultanément à jour le texte de la bannière, le tableau de la politique et les éléments à supprimer en cas de retrait, il n'existe donc aucun endroit où une incohérence peut apparaître.
Les sites qui diffusent des publicités pour générer des revenus (comme AdSense) ne sont pas concernés si ces publicités ciblent l'Union européenne, car un CMP certifié par Google y est alors obligatoire. Pour un site ordinaire, en revanche, cette solution suffit amplement. Pour les sites qui ont déjà défini leurs balises et souhaitent pouvoir auditer eux-mêmes leur gestion du consentement, je pense qu'elle offre une visibilité bien supérieure à celle d'un outil clé en main.
Passé du DTP au Web, il maîtrise aussi bien le balisage que le frontend, la direction de projet et l'accessibilité : un véritable « sage de la technique ». Actif chez Liberogic depuis ses débuts, il est aujourd'hui une référence interne incontournable. Ces derniers temps, il explore avec passion l'optimisation par l'IA : « Ne pourrait-on pas davantage s'appuyer sur l'IA pour l'accessibilité ? » Sa technique comme sa réflexion continuent d'évoluer.
Ayumu Futamata
Spécialiste certifié en accessibilité web IAAP (WAS) / Intégrateur balisage / Ingénieur frontend / Directeur web