Saltar al contenido principal
Principiante TutorialCódigo

Cómo Implementar el Banner de Cookies (RGPD) Correctamente en Tu Web — Paso a Paso

Guía práctica para implementar un banner de consentimiento de cookies que cumpla con RGPD/LOPD. Sin plugins de pago, con código HTML/JS puro, y con checklist legal real.

Fran Cobos 7 min de lectura 1367 palabras

Tabla de contenidos

El 90% de los banners de cookies que veo en webs de desarrolladores están mal implementados. Y no hablo de diseño — hablo de cumplimiento legal. El error más común: cargar Google Analytics antes de que el usuario acepte las cookies.

Yo también lo hacía. Ponía el script de GA en el <head>, mostraba un banner bonito, y cuando el usuario hacía clic en “Aceptar” guardaba un flag en localStorage. El problema: GA ya estaba trackeando desde el primer milisegundo.

Esto es lo que hago ahora, y es lo que exige la RGPD.

El principio fundamental

1. Cargar la web SIN cookies no esenciales
2. Mostrar el banner
3. El usuario elige: Aceptar / Rechazar / Configurar
4. SOLO si acepta → cargar Analytics, AdSense, etc.
5. Guardar la preferencia para no preguntar otra vez

No al revés. Primero el consentimiento, después las cookies.

Implementación completa (HTML + JS puro)

El banner HTML

<!-- Poner al final del <body>, antes de </body> -->
<div id="cookie-banner" class="fixed bottom-0 left-0 right-0 z-50 hidden">
  <div class="mx-auto max-w-5xl px-4 py-4">
    <div class="rounded-2xl border border-gray-200 bg-white p-6 shadow-2xl dark:border-gray-700 dark:bg-gray-900">
      <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
        <div class="flex-1">
          <p class="text-sm text-gray-700 dark:text-gray-300">
            Usamos cookies propias y de terceros para analíticas y personalización.
            Puedes aceptar, rechazar o
            <a href="/cookies" class="underline hover:text-blue-600">configurar tus preferencias</a>.
          </p>
        </div>
        <div class="flex flex-shrink-0 gap-3">
          <button
            id="cookie-reject"
            class="rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
          >
            Rechazar
          </button>
          <button
            id="cookie-accept"
            class="rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-500"
          >
            Aceptar
          </button>
        </div>
      </div>
    </div>
  </div>
</div>

Detalles de diseño que importan legalmente:

  • Los botones de “Aceptar” y “Rechazar” tienen el mismo tamaño visual. Si haces el de aceptar gigante y el de rechazar pequeño/gris, es un dark pattern y la AEPD puede multarte.
  • El texto es claro y directo. Nada de “utilizamos cookies para mejorar tu experiencia” (demasiado vago).
  • Hay enlace a la política de cookies.

El JavaScript

// cookie-consent.js
(function() {
  'use strict';

  const CONSENT_KEY = 'cookie-consent';
  const banner = document.getElementById('cookie-banner');
  const acceptBtn = document.getElementById('cookie-accept');
  const rejectBtn = document.getElementById('cookie-reject');

  // Si ya hay una decisión guardada, no mostrar el banner
  const savedConsent = localStorage.getItem(CONSENT_KEY);

  if (savedConsent === 'accepted') {
    loadAnalytics();
    return;
  }

  if (savedConsent === 'rejected') {
    return; // No cargar nada, no mostrar banner
  }

  // Primera visita: mostrar el banner
  if (banner) {
    banner.classList.remove('hidden');
  }

  if (acceptBtn) {
    acceptBtn.addEventListener('click', function() {
      localStorage.setItem(CONSENT_KEY, 'accepted');
      if (banner) banner.classList.add('hidden');
      loadAnalytics();
    });
  }

  if (rejectBtn) {
    rejectBtn.addEventListener('click', function() {
      localStorage.setItem(CONSENT_KEY, 'rejected');
      if (banner) banner.classList.add('hidden');
    });
  }

  function loadAnalytics() {
    // Google Analytics 4
    if (!document.querySelector('script[src*="googletagmanager"]')) {
      var gaScript = document.createElement('script');
      gaScript.async = true;
      gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX';
      document.head.appendChild(gaScript);

      gaScript.onload = function() {
        window.dataLayer = window.dataLayer || [];
        function gtag() { window.dataLayer.push(arguments); }
        window.gtag = gtag;
        gtag('js', new Date());
        gtag('config', 'G-XXXXXXXXXX', {
          anonymize_ip: true  // Anonimizar IP — recomendado por RGPD
        });
      };
    }

    // Aquí puedes cargar otros scripts: AdSense, Facebook Pixel, Hotjar, etc.
  }
})();

Lo importante:

  • loadAnalytics() solo se ejecuta si el usuario acepta o si ya aceptó antes
  • anonymize_ip: true anonimiza la IP del usuario (recomendado por RGPD aunque no es obligatorio desde GA4)
  • Si el usuario rechaza, no se carga nada. Ni ahora ni en visitas futuras
  • El script se ejecuta en un IIFE para no contaminar el scope global

Cómo cargarlo en tu web

<!-- En el <head>, ANTES de cualquier script de tracking -->
<script>
  // NO pongas Google Analytics aquí. Solo el cookie-consent.
</script>

<!-- Al final del <body> -->
<script src="/cookie-consent.js"></script>

En mi caso con Astro, lo cargo así:

---
// BlogLayout.astro
---
<html>
  <head>
    <!-- NO hay GA aquí. Solo se carga si hay consentimiento. -->
  </head>
  <body>
    <slot />

    <!-- Banner de cookies -->
    <div id="cookie-banner" class="...">
      <!-- ... -->
    </div>

    <script is:inline>
      // El script de consentimiento inline
      // (misma lógica que arriba)
    </script>
  </body>
</html>

La política de cookies (página obligatoria)

Necesitas una página /cookies (o /cookies.html) con:

## Política de Cookies

### ¿Qué son las cookies?
Las cookies son pequeños archivos de texto que se almacenan en tu navegador...

### Cookies que utilizamos

| Cookie | Tipo | Proveedor | Propósito | Duración |
|--------|------|-----------|-----------|----------|
| `cookie-consent` | Técnica (propia) | Este sitio | Guardar tu preferencia de cookies | Permanente |
| `_ga` | Analítica (tercero) | Google Analytics | Distinguir usuarios únicos | 2 años |
| `_ga_XXXXXXX` | Analítica (tercero) | Google Analytics | Persistir estado de sesión | 2 años |
| `color-theme` | Técnica (propia) | Este sitio | Guardar preferencia de tema oscuro/claro | Permanente |

### Cómo gestionar las cookies
Puedes cambiar tu preferencia en cualquier momento haciendo clic en [Gestionar cookies].
También puedes configurar tu navegador para bloquear cookies...

### Base legal
El tratamiento se basa en tu consentimiento (art. 6.1.a RGPD).
Las cookies técnicas se basan en interés legítimo (art. 6.1.f RGPD).

### Contacto
Si tienes dudas: [tu email]

Checklist de cumplimiento

Obligatorio

  • El banner aparece en la primera visita
  • NO se cargan cookies de tracking antes del consentimiento
  • Hay opción de rechazar con la misma visibilidad que aceptar
  • La preferencia se guarda y no se pregunta en cada página
  • Hay un enlace a la política de cookies desde el banner
  • Existe una página de política de cookies accesible
  • La política lista todas las cookies con su proveedor, propósito y duración

Recomendado

  • Opción de “configurar” cookies por categoría (analíticas, marketing, etc.)
  • Botón para cambiar la preferencia después (en el footer, por ejemplo)
  • anonymize_ip: true en Google Analytics
  • Las cookies se eliminan si el usuario cambia de “aceptar” a “rechazar”

Prohibido (dark patterns que multa la AEPD)

  • Botón de “Aceptar” mucho más grande/visible que “Rechazar”
  • Solo opción de “Aceptar” y “Más información” (sin rechazar)
  • Cookie wall: “Acepta las cookies o no puedes ver la web”
  • Pre-marcar casillas de cookies no esenciales
  • Requerir más clics para rechazar que para aceptar

Versión avanzada: con categorías

Si quieres ir más allá y permitir que el usuario elija por categorías:

const CATEGORIES = {
  essential: true,   // Siempre activas, no se pueden desactivar
  analytics: false,  // Google Analytics, Hotjar
  marketing: false,  // AdSense, Facebook Pixel
};

function getConsent() {
  const saved = localStorage.getItem('cookie-categories');
  return saved ? JSON.parse(saved) : null;
}

function setConsent(categories) {
  localStorage.setItem('cookie-consent', 'configured');
  localStorage.setItem('cookie-categories', JSON.stringify(categories));

  if (categories.analytics) loadAnalytics();
  if (categories.marketing) loadMarketing();
}

// Aceptar todo
function acceptAll() {
  setConsent({ essential: true, analytics: true, marketing: true });
}

// Rechazar todo (solo esenciales)
function rejectAll() {
  setConsent({ essential: true, analytics: false, marketing: false });
}

Mi implementación real

En mi portfolio uso una versión simple (aceptar/rechazar) porque solo tengo Google Analytics. No necesito categorías si solo tengo un tipo de cookie no esencial.

El código está en mi portfolio con Vite y Tailwind, y el consentimiento se comparte entre el portfolio y el blog porque ambos están en el mismo dominio (francobosg.netlify.app). El localStorage es compartido, así que si aceptas en el portfolio, el blog ya lo sabe.

Herramientas que NO recomiendo

  • CookieBot, OneTrust, Iubenda: Son soluciones SaaS de pago (50-200€/mes). Para una web personal o un blog, es matar moscas a cañonazos. El script que te puse arriba hace exactamente lo mismo.
  • Plugins de WordPress: Si no estás en WordPress, no te sirven. Y si estás en WordPress, muchos cargan 100KB+ de JavaScript para algo que se hace con 30 líneas.
  • Google Consent Mode: Es un layer adicional que Google recomienda para “medir sin cookies”. En la práctica, sigue necesitando consentimiento para tracking completo. Úsalo solo si tienes ads.

Artículos relacionados

Fran Cobos

Fran Cobos

Desarrollador Full Stack especializado en IA aplicada, automatización y desarrollo web. Escribo sobre herramientas, tutoriales y casos reales para programadores.

¿Necesitas desarrollo a medida?

Apps web, IA, módulos ERP — cuéntame tu proyecto.