Tests A/B des bannières de consentement aux cookies
Le défi
Vous souhaitez tester différents designs ou messages pour vos bannières de cookies afin d'améliorer les taux de consentement tout en respectant le RGPD.
Le scénario
« Je souhaite effectuer un test A/B sur notre bannière de cookies pour voir si différentes formulations ont une incidence sur les taux d'acceptation. Mais je m'inquiète de la conformité au RGPD si je trace les utilisateurs avant qu'ils n'aient donné leur consentement. »
Approche de test A/B conforme
Le principe clé : vous pouvez tester les bannières de cookies SANS outils de suivi, en utilisant uniquement les cookies strictement nécessaires.
Mise en œuvre
// Create variant assignment (strictly necessary cookie)
function assignVariant() {
let variant = getCookie('banner_test_variant');
if (!variant) {
// Randomly assign variant
variant = Math.random() < 0.5 ? 'A' : 'B';
// Store in strictly necessary cookie (doesn't require consent)
document.cookie = banner_test_variant=${variant}; path=/; max-age=2592000; SameSite=Strict;
}
return variant;
}
// Configuration for Variant A (Control)
const configA = {
"notice_banner_type": "simple",
"website_name": "My Website",
// ... other config
};
// Configuration for Variant B (Test)
const configB = {
"notice_banner_type": "headline",
"website_name": "My Site", // Shorter name
// ... other config
};
// Initialize based on variant
document.addEventListener('DOMContentLoaded', function () {
const variant = assignVariant();
const config = variant === 'A' ? configA : configB;
// Add callback to track results (only after consent)
config.callbacks = {
"i_agree_button_clicked": () => {
// User accepted - log this (now they've consented to tracking)
logConsentEvent(variant, 'accepted');
},
"i_decline_button_clicked": () => {
// User declined - still log the action
logConsentEvent(variant, 'declined');
}
};
cookieconsent.run(config);
});
function logConsentEvent(variant, action) {
// Send to your analytics (user has now consented)
if (typeof gtag !== 'undefined') {
gtag('event', 'cookie_consent', {
'variant': variant,
'action': action
});
}
// Or send to backend
fetch('/api/log-consent', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
variant: variant,
action: action,
timestamp: new Date().toISOString()
})
});
}
function getCookie(name) {
const value = ; ${document.cookie};
const parts = value.split(; ${name}=);
if (parts.length === 2) return parts.pop().split(';').shift();
}
Variables de test
Type de bannière
// Variant A: Simple banner
"notice_banner_type": "simple"
// Variant B: Headline banner
"notice_banner_type": "headline"
Palette de couleurs
// Variant A: Light
"palette": "light"
// Variant B: Dark
"palette": "dark"
Texte du bouton (via CSS personnalisé)
// Add CSS to change button text
const style = document.createElement('style');
style.textContent = variant === 'A'
? '.cc-nb-okagree::before { content: "Accept All"; }'
: '.cc-nb-okagree::before { content: "I Agree"; }';
document.head.appendChild(style);
Mesure des impressions de consentement
// Backend endpoint to analyze results
// GET /api/consent-analytics
{
"variant_A": {
"impressions": 1000,
"accepted": 650,
"declined": 250,
"no_action": 100,
"acceptance_rate": 0.65
},
"variant_B": {
"impressions": 1000,
"accepted": 720,
"declined": 200,
"no_action": 80,
"acceptance_rate": 0.72
}
}
Consentement interdomaines
Problème
« Nous avons www.example.com pour notre site principal et shop.example.com pour notre boutique. Les utilisateurs voient la bannière relative aux cookies sur les DEUX sites, même après avoir donné leur consentement sur le premier. C'est agaçant et cela nous fait perdre des conversions. »
Comprendre les défis liés aux domaines multiples
Par défaut, les cookies sont limités au domaine spécifique sur lequel ils sont définis :
- Cookie défini sur
www.example.com→ Inaccessible surshop.example.com - Cookie défini sur
shop.example.com→ Inaccessible surwww.example.com
Solution : paramètre cookie_domain
Utilisez le paramètre cookie_domain pour partager le consentement entre les sous-domaines :
cookieconsent.run({
"consent_type": "express",
"website_name": "My Website",
"website_privacy_policy_url": "https://www.example.com/privacy",
"cookie_domain": ".example.com", // Note the leading dot!
"page_load_consent_levels": ["strictly-necessary"]
});
Important ! Le point initial (.example.com) rend le cookie accessible à :
example.comwww.example.comshop.example.comblog.example.com- Tout sous-domaine de
example.com
Un exemple complet inter-domaines
Sur tous les domaines/sous-domaines, utilisez une configuration identique :
<!-- On www.example.com -->
<script type="text/javascript" src="//www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function () {
cookieconsent.run({
"notice_banner_type": "headline",
"consent_type": "express",
"palette": "light",
"language": "en",
"website_name": "Example",
"website_privacy_policy_url": "https://www.example.com/privacy",
"open_preferences_center_selector": "#changePreferences",
"page_load_consent_levels": ["strictly-necessary"],
"cookie_domain": ".example.com", // Shared across all subdomains
"cookie_secure": true // Recommended for production
});
});
</script>
<!-- On shop.example.com - SAME configuration -->
<script type="text/javascript" src="//www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function () {
cookieconsent.run({
// EXACT SAME CONFIG as main domain
"notice_banner_type": "headline",
"consent_type": "express",
"palette": "light",
"language": "en",
"website_name": "Example",
"website_privacy_policy_url": "https://www.example.com/privacy",
"open_preferences_center_selector": "#changePreferences",
"page_load_consent_levels": ["strictly-necessary"],
"cookie_domain": ".example.com", // Same domain!
"cookie_secure": true
});
});
</script>
Tester la configuration inter-domaines
- Rendez-vous sur
www.example.com - Acceptez les cookies
- Ouvrez DevTools → Application → Cookies
- Vérifiez que le domaine du cookie affiche
.example.com(avec un point au début) - Accédez à
shop.example.com - Vérifiez que la bannière n'apparaît PAS (consentement déjà donné)
Problèmes courants liés aux domaines croisés
Problème 1 : la bannière s'affiche toujours sur les sous-domaines
Problème
« J'ai défini cookie_domain sur « .example.com », mais la bannière s'affiche toujours sur tous les sous-domaines. »
Cause
Le navigateur bloque les cookies tiers.
Solution
Assurez-vous que cookie_secure est défini sur true pour les sites HTTPS.
// Make sure cookie_secure is true for HTTPS sites
"cookie_secure": true,
// Also verify in DevTools that cookie is actually being set
// Check: Application → Cookies → Look for cookie_consent_level
// Domain should show: .example.com (not www.example.com)
Problème n° 2 : consentement différent selon les sous-domaines
Problème
L'utilisateur donne son consentement sur le domaine principal, mais ses préférences diffèrent sur le sous-domaine.
Cause
Les configurations ne sont pas identiques d'un domaine à l'autre.
Solution
Utilisez un fichier de configuration ou une variable partagée.
// shared-config.js (include on all domains)
const SHARED_COOKIE_CONFIG = {
"notice_banner_type": "headline",
"consent_type": "express",
"palette": "light",
"language": "en",
"website_name": "Example",
"website_privacy_policy_url": "https://www.example.com/privacy",
"open_preferences_center_selector": "#changePreferences",
"page_load_consent_levels": ["strictly-necessary"],
"cookie_domain": ".example.com",
"cookie_secure": true
};
// Then on each domain:
cookieconsent.run(SHARED_COOKIE_CONFIG);
Multi-domaines (différents TLD)
Problème
Vous avez example.com ET example.co.uk, donc des domaines complètement différents.
Solution
Les cookies NE PEUVENT PAS être partagés entre différents domaines de premier niveau pour des raisons de sécurité du navigateur.
Vous avez deux options :
Option 1 : Accepter plusieurs bannières.
- L'utilisateur voit une bannière sur chaque domaine de premier niveau
- Le consentement donné s'applique individuellement à chaque domaine, il n'est pas partagé
- C'est probablement l'approche la plus conforme
Option 2 : Utiliser une synchronisation en arrière-plan (pour les utilisateurs connectés)
// On example.com - user accepts
"callbacks": {
"user_consent_saved": () => {
if (isUserLoggedIn()) {
// Sync to backend
fetch('https://api.example.com/sync-consent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + getUserToken()
},
body: JSON.stringify({
consent: getConsentState(),
domains: ['example.com', 'example.co.uk']
})
});
}
}
}
// On example.co.uk - check backend first
async function checkBackendConsent() {
if (isUserLoggedIn()) {
const response = await fetch('https://api.example.com/get-consent', {
headers: {'Authorization': 'Bearer ' + getUserToken()}
});
const data = await response.json();
if (data.hasConsent) {
// Set cookie locally
setCookieConsent(data.consentLevel);
}
}
}
Rechargement de la page après le consentement
Problème
« Une fois que l'utilisateur a accepté les cookies, je recharge la page avec window.location.reload() pour m'assurer que les scripts de suivi se chargent. Mais cela fait perdre à Google Analytics la source de référence d'origine et affiche tout le trafic comme « Direct ». »
Cela se produit car lorsque vous rechargez la page :
- Le navigateur efface les informations de référent
- Le chargement de la nouvelle page semble provenir du même domaine
- Google Analytics classe ce trafic dans la catégorie « Direct »
- Vous perdez les données d'attribution
Solution 1 : Ne pas recharger la page (solution recommandée)
Utilisez des callbacks pour charger dynamiquement les scripts au lieu de recharger la page :
cookieconsent.run({
"notice_banner_type": "headline",
"consent_type": "express",
"palette": "light",
"language": "en",
"website_name": "My Site",
"website_privacy_policy_url": "https://mysite.com/privacy",
"page_load_consent_levels": ["strictly-necessary"],
"callbacks": {
"scripts_specific_loaded": (level) => {
// Scripts are NOW loaded - no reload needed!
console.log("Scripts loaded for: " + level);
if (level === 'tracking') {
// Analytics is now active
// Send initial page view if needed
if (typeof gtag !== 'undefined') {
gtag('event', 'page_view', {
'page_location': window.location.href,
'page_referrer': document.referrer
});
}
}
}
},
"callbacks_force": true
});
Solution 2 : Conserver le référent lors du rechargement de la page
Si vous DEVEZ recharger la page, conservez le référent :
cookieconsent.run({
// ... config ...
"callbacks": {
"i_agree_button_clicked": () => {
// Store referrer and source before reload
sessionStorage.setItem('original_referrer', document.referrer);
sessionStorage.setItem('landing_page', window.location.href);
// Get URL parameters (utm_source, etc.)
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('utm_source')) {
sessionStorage.setItem('utm_source', urlParams.get('utm_source'));
sessionStorage.setItem('utm_medium', urlParams.get('utm_medium'));
sessionStorage.setItem('utm_campaign', urlParams.get('utm_campaign'));
}
// Now reload
window.location.reload();
}
}
});
// After reload, restore attribution
document.addEventListener('DOMContentLoaded', function() {
const savedReferrer = sessionStorage.getItem('original_referrer');
const utmSource = sessionStorage.getItem('utm_source');
if (savedReferrer && typeof gtag !== 'undefined') {
// Send page view with preserved referrer
gtag('event', 'page_view', {
'page_location': window.location.href,
'page_referrer': savedReferrer
});
// Send campaign data if available
if (utmSource) {
gtag('event', 'campaign_details', {
'campaign_source': utmSource,
'campaign_medium': sessionStorage.getItem('utm_medium'),
'campaign_name': sessionStorage.getItem('utm_campaign')
});
}
// Clear storage
sessionStorage.removeItem('original_referrer');
sessionStorage.removeItem('utm_source');
// ... clear others
}
});
Solution 3 : Utilisez le mode de consentement de Google (meilleure pratique)
Avec le mode Consent V2, un rechargement n'est PAS nécessaire :
<!-- 1. Set default consent to denied -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied'
});
</script>
<!-- 2. Load Google Analytics (runs in consent mode) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
<!-- 3. Cookie Consent with callbacks -->
<script src="//www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
cookieconsent.run({
"consent_type": "express",
"website_name": "My Site",
"page_load_consent_levels": ["strictly-necessary"],
"callbacks": {
"scripts_specific_loaded": (level) => {
// Update consent mode - NO RELOAD!
switch (level) {
case 'tracking':
gtag('consent', 'update', {
'analytics_storage': 'granted'
});
break;
case 'targeting':
gtag('consent', 'update', {
'ad_storage': 'granted',
'ad_user_data': 'granted',
'ad_personalization': 'granted'
});
break;
}
}
},
"callbacks_force": true
});
});
</script>
Contenu intégré et iFrames
Problème
« Nous avons des vidéos YouTube intégrées sur notre site. Devrions-nous bloquer les iframes jusqu'à ce que les utilisateurs acceptent les cookies ? Comment mettre cela en œuvre avec TermsFeed ? »
Solution
La solution consiste à intégrer un espace réservé et à utiliser le chargement dynamique.
Étape 1 : Créer un espace réservé HTML
<!-- Instead of directly embedding iframe -->
<div class="video-placeholder" data-video-id="dQw4w9WgXcQ" data-consent-type="targeting">
<div class="placeholder-content">
<img src="/images/video-thumbnail.jpg" alt="Video thumbnail">
<div class="placeholder-overlay">
<button class="accept-and-load" onclick="loadVideo(this)">
Accept cookies to watch this video
</button>
<p>This video is hosted by YouTube and requires marketing cookies.</p>
<a href="#" onclick="openCookiePreferences(); return false;">
Change cookie preferences
</a>
</div>
</div>
</div>
<style>
.video-placeholder {
position: relative;
width: 100%;
padding-bottom: 56.25%; // 16:9 aspect ratio
background: #000;
}
.placeholder-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.placeholder-content img {
width: 100%;
height: 100%;
object-fit: cover;
}
.placeholder-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
text-align: center;
padding: 20px;
}
.accept-and-load {
background: #ff0000;
color: white;
border: none;
padding: 15px 30px;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
margin-bottom: 15px;
}
</style>
Étape 2 : Charger l'iframe après le consentement
// Check if user has already consented
function hasConsent(consentType) {
const consentCookie = getCookie('cookie_consent_level');
if (!consentCookie) return false;
// Parse consent levels
const levels = JSON.parse(decodeURIComponent(consentCookie));
return levels.includes(consentType);
}
// Load video dynamically
function loadVideo(button) {
const placeholder = button.closest('.video-placeholder');
const videoId = placeholder.dataset.videoId;
const consentType = placeholder.dataset.consentType;
// Check if user has consent
if (!hasConsent(consentType)) {
// Request consent
if (typeof cookieconsent !== 'undefined' && cookieconsent.openPreferencesCenter) {
cookieconsent.openPreferencesCenter();
} else {
alert('Please accept marketing cookies to view this content');
}
return;
}
// Create iframe
const iframe = document.createElement('iframe');
iframe.src = https://www.youtube.com/embed/${videoId};
iframe.width = '100%';
iframe.height = '100%';
iframe.frameBorder = '0';
iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
iframe.allowFullscreen = true;
// Replace placeholder with iframe
placeholder.innerHTML = '';
placeholder.appendChild(iframe);
}
// Auto-load videos if consent already given
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.video-placeholder').forEach(placeholder => {
const consentType = placeholder.dataset.consentType;
if (hasConsent(consentType)) {
// Auto-load - user already consented
const videoId = placeholder.dataset.videoId;
const iframe = document.createElement('iframe');
iframe.src = https://www.youtube.com/embed/${videoId};
iframe.width = '100%';
iframe.height = '100%';
iframe.frameBorder = '0';
iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
iframe.allowFullscreen = true;
placeholder.innerHTML = '';
placeholder.appendChild(iframe);
}
});
});
// Listen for consent updates
document.addEventListener('DOMContentLoaded', function() {
cookieconsent.run({
// ... config ...
"callbacks": {
"scripts_specific_loaded": (level) => {
// When user accepts targeting cookies, load all pending videos
if (level === 'targeting') {
document.querySelectorAll('.video-placeholder').forEach(placeholder => {
if (placeholder.dataset.consentType === 'targeting') {
loadVideo(placeholder.querySelector('.accept-and-load'));
}
});
}
}
}
});
});
function getCookie(name) {
const value = ; ${document.cookie};
const parts = value.split(; ${name}=);
if (parts.length === 2) return parts.pop().split(';').shift();
}
function openCookiePreferences() {
if (typeof cookieconsent !== 'undefined' && cookieconsent.openPreferencesCenter) {
cookieconsent.openPreferencesCenter();
}
}
Exemple pour d'autres intégrations
Google Maps
<div class="map-placeholder" data-consent-type="functionality" data-location="New York, NY">
<div class="placeholder-overlay">
<p>️ Accept functional cookies to view map</p>
<button onclick="loadMap(this)">Load Map</button>
</div>
</div>
<script>
function loadMap(button) {
const placeholder = button.closest('.map-placeholder');
const location = placeholder.dataset.location;
if (!hasConsent('functionality')) {
cookieconsent.openPreferencesCenter();
return;
}
const iframe = document.createElement('iframe');
iframe.src = https://www.google.com/maps/embed/v1/place?key=YOUR_API_KEY&q=${encodeURIComponent(location)};
iframe.width = '100%';
iframe.height = '450';
iframe.style.border = '0';
iframe.allowFullscreen = true;
iframe.loading = 'lazy';
placeholder.innerHTML = '';
placeholder.appendChild(iframe);
}
</script>
Intégrations de réseaux sociaux (X, Instagram, Facebook)
<div class="social-placeholder" data-consent-type="targeting" data-platform="twitter" data-url="https://twitter.com/user/status/123">
<div class="placeholder-overlay">
<p> Accept marketing cookies to view this tweet</p>
<button onclick="loadSocialEmbed(this)">Load Tweet</button>
</div>
</div>
<script>
function loadSocialEmbed(button) {
const placeholder = button.closest('.social-placeholder');
const platform = placeholder.dataset.platform;
const url = placeholder.dataset.url;
if (!hasConsent('targeting')) {
cookieconsent.openPreferencesCenter();
return;
}
// Load platform SDK
if (platform === 'twitter' && !window.twttr) {
const script = document.createElement('script');
script.src = 'https://platform.twitter.com/widgets.js';
script.onload = () => {
twttr.widgets.createTweet(
url.split('/').pop(),
placeholder,
{ theme: 'light' }
);
};
document.head.appendChild(script);
}
// Add other platforms as needed
}
</script>