Pruebas A/B de los banners de consentimiento de cookies
El reto
Quieres probar diferentes diseños o mensajes de los banners de cookies para mejorar las tasas de consentimiento sin dejar de cumplir con el RGPD.
El escenario
«Quiero realizar una prueba A/B de nuestro banner de cookies para ver si diferentes formulaciones afectan a las tasas de aceptación. Pero me preocupa el cumplimiento del RGPD si realizo un seguimiento de los usuarios antes de que den su consentimiento».
Enfoque de pruebas A/B conforme a la normativa
El principio clave: puedes probar los banners de cookies SIN herramientas de seguimiento, utilizando únicamente las cookies estrictamente necesarias.
Implementación
// 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 prueba
Tipo de banner
// Variant A: Simple banner
"notice_banner_type": "simple"
// Variant B: Headline banner
"notice_banner_type": "headline"
Esquema de colores
// Variant A: Light
"palette": "light"
// Variant B: Dark
"palette": "dark"
Texto del botón (mediante CSS personalizado)
// 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);
Medición de impresiones de consentimiento
// 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
}
}
Consentimiento entre dominios
Problema
«Tenemos www.example.com para nuestro sitio web principal y shop.example.com para nuestra tienda. Los usuarios ven el banner de cookies en AMBOS sitios, incluso después de haberlo aceptado en el primero. Es molesto y estamos perdiendo conversiones».
Comprender los retos entre dominios
Por defecto, las cookies se limitan al dominio específico en el que se establecen:
- Cookie establecida en
www.example.com→ No accesible enshop.example.com - Cookie establecida en
shop.example.com→ No accesible enwww.example.com
Solución: parámetro cookie_domain
Utilice el parámetro cookie_domain para compartir el consentimiento entre subdominios:
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"]
});
¡Importante! El punto inicial (.example.com) hace que la cookie sea accesible para:
example.comwww.example.comshop.example.comblog.example.com- Cualquier subdominio de
example.com
Un ejemplo completo entre dominios
En todos los dominios/subdominios, utilice una configuración idéntica:
<!-- 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>
Prueba de la configuración entre dominios
- Visite
www.example.com - Acepta las cookies
- Abre DevTools → Aplicación → Cookies
- Comprueba que el dominio de la cookie muestre
.example.com(con un punto al principio) - Navega a
shop.example.com - Comprueba que el banner NO aparece (consentimiento ya otorgado)
Problemas comunes entre dominios
Problema 1: El banner sigue apareciendo en los subdominios
Problema
«He configurado cookie_domain como '.example.com', pero el banner sigue apareciendo en todos los subdominios».
Causa
El navegador está bloqueando las cookies de terceros.
Solución
Asegúrate de que cookie_secure sea true para los sitios 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)
Problema 2: Consentimiento diferente en distintos subdominios
Problema:
El usuario da su consentimiento en el dominio principal, pero las preferencias difieren en el subdominio.
Causa:
Las configuraciones no son idénticas en todos los dominios.
Solución:
Utilice un archivo de configuración o una variable compartida.
// 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);
Multidominio (diferentes TLD)
Problema:
Tienes example.com Y example.co.uk, es decir, dominios completamente diferentes.
Solución:
Las cookies NO PUEDEN compartirse entre diferentes dominios de nivel superior debido a la seguridad del navegador.
Tienes dos opciones:
Opción 1: Aceptar varios banners.
- El usuario ve un banner en cada dominio de nivel superior
- El consentimiento otorgado se aplica individualmente para cada dominio; el consentimiento no se comparte
- Probablemente sea el enfoque que mejor cumple con la normativa
Opción 2: Utilizar una sincronización en el backend (para usuarios que hayan iniciado sesión)
// 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);
}
}
}
Recarga de la página tras el consentimiento
Problema
«Después de que el usuario acepta las cookies, recargo la página con window.location.reload() para asegurarme de que se carguen los scripts de seguimiento. Pero esto hace que Google Analytics pierda la fuente de referencia original y muestre todo el tráfico como "Directo"».
Esto ocurre porque, al recargar la página:
- El navegador borra la información de referencia
- La nueva carga de la página parece provenir del mismo dominio
- Google Analytics lo clasifica como tráfico «Directo»
- Se pierden los datos de atribución
Solución 1: No recargues la página (opción recomendada)
Utiliza callbacks para cargar scripts de forma dinámica en lugar de recargar:
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
});
Solución 2: Conserva el referrer al recargar la página
Si TIENES que recargar, conserva el referrer:
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
}
});
Solución 3: Utiliza el Modo de consentimiento de Google (práctica recomendada)
Con el Modo de consentimiento V2, NO es necesario recargar la página:
<!-- 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>
Contenido incrustado e iFrames
Problema
«Tenemos vídeos de YouTube incrustados en nuestro sitio web. ¿Deberíamos bloquear los iframes hasta que los usuarios acepten las cookies? ¿Cómo implementamos esto con TermsFeed?»
Solución
La solución consiste en integrar un marcador de posición y utilizar la carga dinámica.
Paso 1: Crear el marcador de posición 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>
Paso 2: Cargar el iframe tras el consentimiento
// 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();
}
}
Ejemplo para otras incrustaciones
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>
Incrustaciones de redes sociales (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>