Suivi sans cookie pour les Iframes intersites
![](https://www.reklam.fr/wp-content/uploads/2023/01/iframe-postmessage-780x470.jpg)
Mis à jour le 25 mai 2021: Ajout d’informations sur son utilisation avec GA4. Comme Google Analytics 4 ne dispose pas d’un mécanisme pour désactiver le stockage des cookies, seule la deuxième solution (envoyer
dataLayer
événements de l’iframe au parent) décrits dans cet article fonctionneront pour GA4.
Me voilà de retour avec <iframe>
et suivi inter-domaines. J’ai déjà publié quelques articles sur le sujet, ma solution mise à jour étant la plus récente. Ces articles abordent le problème général de transmettre l’ID client du parent au <iframe>
.
Ce faisant, le <iframe>
peut prendre l’ID client de l’URL du cadre et créer le _ga
biscuit dans le <iframe>
autorisant les accès du parent et les <iframe>
d’utiliser le même identifiant client. Génial.
Cependant, il y a un hic qui devient de plus en plus troublant avec l’augmentation des mécanismes de protection contre le pistage des navigateurs : si le <iframe>
charge le contenu intersitele navigateur doit autoriser l’accès aux cookies dans un contexte tiers. En d’autres termes, si le navigateur bloque cookies tiersles <iframe>
ne pourra pas écrire le cookie, et le suivi de Google Analytics échouera.
Cet article propose deux solutions à ce problème.
Les première est qu’il envoie l’identifiant client Google Analytics du parent au <iframe>
en utilisant window.postMessage
et le cadre enfant peut interroger ces informations sur chaque page, ce qui signifie que les cookies ne sont pas nécessaires pour stocker l’ID client.
Les seconde solution est que le cadre enfant envoie en fait chaque dataLayer
message au parent, afin que le cadre enfant n’ait pas à suivre quoi que ce soit par lui-même. Le parent gère le suivi du cadre enfant à la place.
En utilisant window.postMessage
est plus robuste que la décoration de lien de mes propositions précédentes, car elle ne force pas le rechargement du cadre et ne nécessite pas non plus que le cadre enfant prenne en charge les cookies. C’est un peu plus compliqué à mettre en place, cependant.
Avertissement! Je ne plaisante pas à propos de cette dernière déclaration. Cette configuration nécessite un peu de code personnalisé et vous travaillez avec une communication bilatérale entre deux fenêtres. Veuillez lire attentivement cet article afin de comprendre ce qui se passe avant de copier-coller du code dans votre ou vos conteneurs Google Tag Manager.
Quel est le problème exactement?
Heureux que vous ayez demandé !
Lorsqu’une page charge un <iframe>
de intersite d’origine, ce cadre est chargé dans un contexte tiers, et tout accès au stockage du navigateur à partir de ce <iframe>
exigera que le navigateur autorise les cookies tiers pour le <iframe
placer.
Un terme clé ici est intersite. Cela signifie que le top nom de domaine sous contrôle privé du <iframe>
est différent de celui de la page parent.
Dans les exemples ci-dessous, le premier nom de domaine sous contrôle privé est en italique.
- www.simoahava.com
- www.gtmtools.com
- www.ebay.co.uk
- sahava.github.io
- analytique.google.com
Si l’un des sites ci-dessus a chargé un autre site de la liste dans un <iframe>
ce contenu serait chargé d’un site à l’autre et toute opération de cookie dans la page intégrée nécessiterait l’accès aux cookies tiers.
Les exemples suivants sont tous même sitemême s’ils sont d’origine croisée :
- www.simoahava.com
- simoahava.com
- Blog.simoahava.com
- tracking.endpoint.simoahava.com
Toute communication entre les pages de la liste ci-dessus se produirait dans un même site (ou de première partie), et l’accès aux cookies ne serait pas limité.
Comme je l’ai mentionné dans l’introduction, cela ne fera que condamner le suivi dans le contenu intégré en raison de la façon dont les navigateurs implémentent les protections de suivi.
Sur www.cookiestatus.com
Même si Google a récemment fait une véritable non-annonce en disant qu’il supprimerait progressivement les cookies tiers d’ici 2022, Google Chrome rendra en fait les choses plus difficiles pour l’accès aux cookies intersites beaucoup, beaucoup plus tôt.
Chrome v80 (publié le 4 février 2020), applique les restrictions de cookies SameSite, ce qui signifie que si un cookie doit être accessible dans un contexte tiers, il nécessite le SameSite=None
et Secure
ensemble de drapeaux. Par défaut, ce sont ne pas Positionner.
Toi pouvez définir ces drapeaux avec le
cookieFlags
paramètre, mais notez que cela ne résout que le “problème” de Google Chrome. Cela ne fait pas fonctionner ces cookies dans les navigateurs qui bloquent les cookies tiers.
Heureusement, il existe un moyen de contourner cela.
Nous pouvons ignorer complètement les cookies.
Solution 1 : Transmettre l’ID client du parent à l’enfant
REMARQUE! Comme il n’existe actuellement aucun mécanisme pour désactiver le stockage des cookies dans les déploiements de Google Analytics 4, cette solution ne fonctionnera que pour Universal Analytics. Pour GA4, vous devez utiliser la solution 2 ci-dessous.
Jetons un coup d’œil à une autre illustration célèbre de votre serviteur :
Il y a beaucoup de potentiel conditions de course dans le mélange, certaines précautions doivent donc être prises. Voici comment fonctionne la page parent :
- La page parent commence à écouter les messages de la
<iframe>
dès que Google Tag Manager se charge. - Une fois que la page parent reçoit le
childReady
message, il commence à interroger le tracker Google Analytics jusqu’à ce qu’un délai d’expiration maximum soit atteint. - Dès que le tracker Google Analytics est disponible, la page mère renvoie l’identifiant client au
<iframe>
.
Et voici comment le <iframe>
la page réagit :
- Les
<iframe>
la page commence à envoyer lechildReady
message dès que Google Tag Manager se charge. - Une fois que la page parent répond avec l’ID client (ou qu’un délai d’attente est atteint), la page enfant arrête d’envoyer le message.
- La page enfant écrit l’ID client dans
dataLayer
.
Nous savons maintenant comment l’ID client sera transmis au <iframe>
mais cela ne suffit pas encore.
Chaque balise Google Analytics dans le <iframe>
doit être configuré pour fonctionner avec cette configuration !
Plus précisément, ils ont tous besoin de deux des champs défini, de préférence dans une variable Paramètres Google Analytics :
storage
mis ànone
pour éviter que le tracker échoue s’il ne peut pas écrire le cookie d’identification client.clientId
mis à la valeur de ladataLayer
.
Nous y reviendrons sous peu, ne vous inquiétez pas.
Solution 2 : transférer tous les messages de la couche de données de l’enfant au parent
Si vous ne voulez pas faire de suivi dans le <iframe>
(je ne vous blâme pas), vous pouvez en fait déléguer le suivi au parent en envoyant tous dataLayer
messages au parent pour traitement.
Il s’agit de la seule solution viable pour suivre le trafic iframe intersite avec GA4.
Cela signifie que la page parent gérerait les balises pour les interactions natives de la page parent aussi bien que ceux qui se produisent dans le <iframe>
.
Le processus est presque le même qu’avec la première solution. Voici comment fonctionne la page parent :
- La page parent commence à écouter les messages de la
<iframe>
dès que Google Tag Manager se charge. - Une fois que la page parent reçoit le
childReady
message, il répond par unparentReady
message. - Si le cadre enfant envoie un message dans
dataLayer
-format compatible, la page parent pousse ce message dans son propredataLayer
.
Sur le <iframe>
voici ce qui se passe :
- Les
<iframe>
la page commence à envoyer lechildReady
message dès que Google Tag Manager se charge. - Une fois que la page parent répond avec
parentReady
le cadre enfant “détourne” ledataLayer.push()
et envoie tous les messages qui lui sont transmis à la page parent.
Les messages du cadre enfant sont nommés pour les garder séparés du “natif” du parent dataLayer
messages, et ils incluent des métadonnées sur le <iframe>
(principalement l’URL et le titre).
La configuration de la page parent
Cet article combine tous les deux solutions en un seul ensemble de balises HTML personnalisées, une pour la page parent et une pour l’enfant <iframe>
.
Sur la page parent, c’est-à-dire la page envoyant l’identifiant client au <iframe>
et en attendant les messages envoyés par l’enfant, vous devez créer un Balise HTML personnalisée qui tire sur un Affichage des pages déclencheur. Vous pouvez utiliser le Toutes les pages déclencheur si vous le souhaitez, mais vous pouvez tout aussi bien créer un Affichage des pages déclencheur qui ne se déclenche que sur les pages dont vous connaissez le <iframe>
exister.
La balise HTML personnalisée
La balise HTML personnalisée elle-même doit contenir le code suivant :
<script> (function() { // Universal Analytics tracking ID whose _ga cookie to use. // If using GA4, you can leave this setting untouched. var trackingId = 'UA-XXXXX-Y'; // Maximum time in milliseconds to wait for GA tracker to load. // Again, irrelevant for GA4. var maxGATime = 2000; // Set to the origin ("https://www.domain.com") of the iframe you want to communicate with var childOrigin = 'https://www.iframe-domain.com'; // Don't touch anything that follows var pollInterval = 200; var postCallback = function(event) { if (event.origin !== childOrigin) return; if (event.data !== 'childReady' && !event.data.event) return; if (event.data === 'childReady') { // Send event that parent is ready event.source.postMessage('parentReady', event.origin); var pollCallback = function() { // Stop polling if maxTime reached maxGATime -= pollInterval; if (maxGATime <= 0) window.clearInterval(poll); // Only proceed if GA loaded and tracker accessible var ga = window[window['GoogleAnalyticsObject']]; if (ga && ga.getAll) { // Get tracker that matches the Tracking ID you provided var tracker = ga.getAll().filter(function return t.get('trackingId') === trackingId; }).shift(); // Send message back to frame with Client ID if (tracker) { event.source.postMessage({ event: 'clientId', clientId: tracker.get('clientId') }, event.origin); } // Stop polling if not already done so window.clearInterval(poll); } }; // Start polling for Google Analytics tracker var poll = window.setInterval(pollCallback, pollInterval) } // Push dataLayer message from iframe to dataLayer of parent if (event.data.event) { window.dataLayer.push(event.data); } }; // Start listening for messages from child frame window.addEventListener('message', postCallback); })(); </script>
Il se passe pas mal de choses ici, alors passons en revue le code ! Si vous ne vous souciez pas de cette analyse approfondie, vous pouvez passer directement à la manière dont vous devrez peut-être configurer le conteneur de page parent pour prendre en charge la configuration du transfert de message.
Configuration
Tout d’abord, il y a les éléments de configuration :
// Tracking ID whose _ga cookie to use var trackingId = 'UA-XXXXX-Y'; // Maximum time in milliseconds to wait for GA tracker to load var maxGATime = 2000; // Set to the origin ("https://www.domain.com") of the iframe you want to communicate with var childOrigin = 'https://www.iframe-domain.com';
Les trackingId
doit être défini sur l’identifiant de suivi Google Analytics du traceur dont _ga
cookie que vous souhaitez utiliser. En effet, plusieurs cookies GA peuvent stocker chacun l’ID client pour un ID de suivi différent. Si vous n’êtes pas sûr, tapez simplement votre ID de suivi habituel comme valeur du trackingId
variable.
Ensemble maxGATime
au temps maximum pendant lequel la page attend le chargement de Google Analytics. Vous pouvez certainement régler cela beaucoup plus haut que 2000 (2 secondes) si vous le souhaitez, mais je déconseille les sondages indéfinis.
Les
trackingId
etmaxGATime
les paramètres ne sont pertinents que pour Universal Analytics. Si vous effectuez un suivi vers GA4, vous n’avez pas besoin de modifier ces valeurs par défaut.
Met le childOrigin
au origine du <iframe>
vous souhaitez envoyer les données. Les origine est tout dans l’URL jusqu’au premier chemin sabrer. Ainsi, si l’URL est https://www.simoahava.com/my-home-page/
les origine serait https://www.simoahava.com
.
Il est important de ne pas avoir la barre oblique finale, car cela fait partie de la chemin composant et non le origine.
L’auditeur
Sur la dernière ligne du bloc de code principal, nous ajoutons le message
auditeur:
window.addEventListener('message', postCallback);
Cela signifie que lorsqu’un <iframe>
envoie un postMessage
à la page parent, le listener déclenche et exécute le postCallback
une fonction.
var postCallback = function(event) { if (event.origin !== childOrigin) return; if (event.data !== 'childReady' && !event.data.event) return; if (event.data === 'childReady') { // Send event that parent is ready event.source.postMessage('parentReady', event.origin); var pollCallback = function() { // Stop polling if maxTime reached maxGATime -= pollInterval; if (maxGATime <= 0) window.clearInterval(poll); // Only proceed if GA loaded and tracker accessible var ga = window[window['GoogleAnalyticsObject']]; if (ga && ga.getAll) { // Get tracker that matches the Tracking ID you provided var tracker = ga.getAll().filter(function return t.get('trackingId') === trackingId; }).shift(); // Send message back to frame with Client ID if (tracker) { event.source.postMessage({ event: 'clientId', clientId: tracker.get('clientId') }, event.origin); } // Stop polling if not already done so window.clearInterval(poll); } }; // Start polling for Google Analytics tracker var poll = window.setInterval(pollCallback, pollInterval) } // Push dataLayer message from iframe to dataLayer of parent if (event.data.event) { window.dataLayer.push(event.data); } };
Tout d’abord, si le message récupéré n’était pas attendu (par exemple, venant d’un autre <iframe>
ou a le mauvais contenu), le rappel arrête l’exécution.
Si le message a été du <iframe>
la première chose à faire est de notifier à la trame enfant que le parent a reçu le message et est prêt à démarrer une communication bilatérale.
Ensuite, un window.setInterval
commence à interroger la page parent toutes les 200 millisecondes jusqu’à la valeur par défaut de 2 secondes complètes. A chaque sondage, le script vérifie si le tracker Google Analytics a été créé. Si c’est le cas, le script prend le clientId
du tracker, et le renvoie au <iframe>
en utilisant event.source.postMessage()
. Une fois cette opération effectuée, l’interrogation est arrêtée manuellement pour éviter que l’ID client ne soit envoyé plusieurs fois au <iframe>
.
Essentiellement, la page parent doit attendre deux choses :
- Pour que la page enfant signale qu’elle est prête à recevoir des messages.
- Pour que le tracker Google Analytics soit disponible, afin que l’ID client puisse en être extrait.
Le dernier bloc de code vérifie si le message du cadre enfant contient un objet avec le event
propriété, auquel cas il pousse cet objet entier dans la page parent dataLayer
.
Configuration du système de transfert de messages
La page parent écoute dataLayer
messages transférés depuis le système intégré <iframe>
. Vous pouvez créer des balises, des déclencheurs et des variables qui réagissent à ces messages.
Tous les déclencheurs seront de type Déclencheur d’événement personnalisé. C’est parce que tous les événements envoyés depuis le cadre seront ajoutés avec le iframe.
préfixe – même ceux sans event
valeur (ils seront envoyés en tant que iframe.Message
). Ainsi, si vous souhaitez déclencher une balise lorsqu’un lien est cliqué dans le <iframe>
le déclencheur pourrait ressembler à ceci :
Cela nécessite un peu de configuration manuelle pour que tout soit opérationnel. Mais cela peut rendre l’ensemble de votre configuration beaucoup plus fluide, car vous n’aurez pas besoin d’intégrer de code supplémentaire dans le <iframe>
.
Chaque message envoyé depuis le <iframe>
est automatiquement enrichi de certaines informations au niveau de la page :
{ iframe: { pageData: { url: 'https://www.iframe-domain.com/iframe-page?iframequery=iframevalue#iframeHash', title: 'The Iframe Page Title | Iframe Company' } } }
Si vous souhaitez mettre à jour les balises de votre page parent pour utiliser le <iframe>
données au niveau de la page, vous devez créer des variables de couche de données pour iframe.pageData.url (l’URL du <iframe>
pages) et iframe.pageData.title (le titre de la page).
Une fois que vous avez tout configuré, vous êtes prêt à configurer le <iframe>
page!
La configuration de la page intégrée (enfant)
C’est là que les choses se compliquent. Dans ce guide, nous couvrons un cas d’utilisation assez typique où le <iframe>
le contenu n’interagit qu’en tant qu’intégration. Il n’y a donc aucun scénario où l’utilisateur visiterait la page chargée dans le <iframe>
dans un contexte first-party ou top-frame. Je discuterai également brièvement de ce scénario plus tard, mais pour l’instant supposons que la page dans le <iframe>
n’est accessible qu’en tant qu’élément intégré.
Vous aurez besoin de faire Trois choses dans le conteneur Google Tag Manager du <iframe>
page.
- Créez une balise HTML personnalisée qui communique avec la page parent.
- Mettre à jour les paramètres de tous de vos balises Universal Analytics (et App+Web pendant que vous y êtes).
- Mettez à jour les déclencheurs de vos balises Universal Analytics afin qu’elles ne se déclenchent pas tant qu’elles n’ont pas reçu l’ID client du parent.
La balise HTML personnalisée
Créez une nouvelle balise HTML personnalisée et configurez-la pour qu’elle se déclenche sur le Toutes les pages déclencheur. Si la <iframe>
est une application d’une seule page, vous ne devez toujours déclencher que la balise HTML personnalisée sur le déclencheur Toutes les pages, et non à chaque changement de page SPA, par exemple.
Voici ce que vous devez copier-coller dans le tag :
<script> (function() { // If not in iframe, do nothing try { if (window.top === window.self) return; } catch(e) {} // Set to false to prevent dataLayer messages from being sent to parent var sendDataLayerMessages = true; // Set the prefix that will be used in the event name, and under which all // the dataLayer properties will be embedded var dataLayerMessagePrefix = 'iframe'; // Set to parent origin ("https://www.domain.com") var parentOrigin = 'https://www.parent-domain.com'; // Maximum time in milliseconds to poll the parent frame for ready signal var maxTime = 2000; // Don't touch anything that follows var pollInterval = 200; var parentReady = false; var postCallback = function(event) { if (event.origin !== parentOrigin) return; if (event.data.event !== 'clientId' && event.data !== 'parentReady') return; if (event.data.event === 'clientId') { window.dataLayer.push({ event: 'clientId', clientId: event.data.clientId }); } if (event.data === 'parentReady' && !parentReady) { window.clearInterval(poll); if (sendDataLayerMessages) startDataLayerMessageCollection(); parentReady = true; } }; var pollCallback = function() { // If maximum time is reached, stop polling maxTime -= pollInterval; if (maxTime <= 0) window.clearInterval(poll); // Send message to parent that iframe is ready to retrieve Client ID window.top.postMessage('childReady', parentOrigin); }; var createMessage = function(obj) { if (!Array.isArray(obj) && typeof obj === 'object') { var flattenObj = JSON.parse(JSON.stringify(obj)); var message = {}; // Add metadata about the page into the message message[dataLayerMessagePrefix] = { pageData: { url: document.location.href, title: document.title } }; for (var prop in flattenObj) { if (flattenObj.hasOwnProperty(prop) && prop !== 'gtm.uniqueEventId') { if (prop === 'event') { message.event = dataLayerMessagePrefix + '.' + flattenObj[prop]; } else { message[dataLayerMessagePrefix][prop] = flattenObj[prop]; } } } if (!message.event) message.event =...