La Content Security Policy (CSP) es una cabecera HTTP de seguridad que indica al navegador qué recursos puede cargar y ejecutar en una página web. Cuando está mal configurada o no incluye los dominios de herramientas de analítica como Google Tag Manager, GA4 o el píxel de Meta, bloquea silenciosamente el envío de datos: los eventos se disparan en GTM pero nunca llegan a su destino. Para solucionarlo hay que añadir los dominios necesarios en las directivas script-src, connect-src e img-src de la CSP, siempre desde el servidor, ya que ningún script puede modificar esta política una vez que el navegador la ha procesado.
Hay un tipo de problema en analítica web especialmente frustrante: el que no da error visible, el que no aparece en ningún informe, el que simplemente hace que los datos dejen de llegar. Sin aviso. Sin fallo aparente en GTM. Sin nada.
Ese problema, cada vez más frecuente, tiene nombre: Content Security Policy (CSP).
En este artículo vamos a entender qué es la CSP desde cero, cómo funciona su sintaxis de directivas *-src, por qué su ausencia total es tan peligrosa como su mala configuración, y qué puedes hacer tú —sin tocar el servidor— para diagnosticar y proponer la solución al equipo que la gestiona.
Primero lo primero: ¿qué es exactamente la CSP?
La Content Security Policy es una cabecera de seguridad HTTP que el servidor envía al navegador junto con cada carga de página. Su función es decirle al navegador qué recursos tiene permiso para cargar y ejecutar en esa página. Según tengamos definida la CSP, puede que debamos permitir expresamente cada uno de los recursos de medición, visualización, chats, encuestas, etc. Sin una CSP correcta, la web puede dejar de funcionar por completo, no solo se trata de medición, con los archivos JS, hojas de estilo, etc. damos forma y funcionalidad a una web.
Cuando el navegador recibe esa cabecera, actúa como un filtro: antes de cargar cualquier script, imagen, fuente o conexión externa, consulta la política. Si el recurso está en la lista permitida, pasa. Si no está, se bloquea. Sin negociación, sin preguntar al usuario.
La motivación original es la protección contra ataques XSS (Cross-Site Scripting): inyecciones de código malicioso que aprovechan que el navegador no distingue entre JavaScript legítimo y el que un atacante ha colado en la página. Con una CSP bien configurada, incluso si alguien consigue inyectar código, el navegador simplemente no lo ejecuta si no cumple las reglas.
El problema para la analítica es que ese mismo mecanismo protector puede bloquear perfectamente tus píxeles de Meta, tu etiqueta de GA4, tu script de VWO o cualquier Custom HTML Tag de GTM mediante el que insertas un píxel de medición.
El estado por defecto: sin CSP, todo está permitido
Aquí viene algo que mucha gente no sabe y que es fundamental para entender el problema:
Si tu web no tiene ninguna cabecera CSP configurada, el navegador permite absolutamente todo.
Cualquier script puede ejecutarse. Cualquier dominio externo puede cargar recursos. Cualquier eval() puede correr (lo que influye también en calcular una variable dinámica, comos las variables de JS personalizado de GTM). No tener una CSP es el estado «sin portero»: entra quien quiera.
Esto tiene dos lecturas. La primera, positiva: tu tracking probablemente funciona sin fricción. La segunda, preocupante: tu web es vulnerable a que un script malicioso inyectado haga lo que quiera.
El movimiento de la industria es claro: los navegadores y los equipos de seguridad están empujando hacia webs con CSP configurada. Lo que antes era opcional está convirtiéndose en estándar. Y cuando alguien en IT activa una CSP sin coordinar con el equipo de analítica, los datos desaparecen de golpe.
La anatomía de una CSP: las directivas *-src
Aquí está el núcleo técnico fuerte que necesitas entender para poder ayudar a tu web o a tus clientes en esta configuración. Una CSP se compone de directivas, y cada directiva controla un tipo específico de recurso.
La sintaxis general es:
Content-Security-Policy: directiva1 valor1 valor2; directiva2 valor1; directiva3 'self'
Cada directiva va seguida de los orígenes permitidos para ese tipo de recurso, separados por espacios. Las directivas se separan entre sí con punto y coma.
default-src
Es la directiva madre, el fallback. Si el navegador busca la política para un tipo de recurso concreto (por ejemplo, connect-src) y no la encuentra en la CSP, cae a default-src. Esto es crítico: si tienes default-src ‘none’ y no has declarado explícitamente las demás directivas, estás bloqueando todo por defecto.
Content-Security-Policy: default-src 'none'
El efecto: la web no cargará nada que no esté en una directiva específica. Escenario común en webs muy seguras, y devastador para el tracking si no se complementa correctamente.
script-src
Controla qué scripts JavaScript pueden ejecutarse. Es la directiva más importante para GTM y el resto de herramientas de analítica.
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com
Lo que debes saber:
- ‘self’ permite únicamente scripts del mismo dominio que la web. No cubre subdominios.
- Si no está declarada y existe default-src, se hereda de ahí.
- Si no hay ni script-src ni default-src: todo está permitido para scripts.
- Aquí son fundamentales los permisos para unsafe-inline (scripts inline), unsafe-eval (funciones dinámicas) y los nonce y hash (métodos seguros de autorización granular).
script-src es la directiva que rompe GTM cuando falta el dominio de Google Tag Manager.
connect-src
Controla las conexiones de red que puede hacer el JavaScript de la página: peticiones fetch, XMLHttpRequest, WebSockets, etc.
connect-src 'self' https://www.google-analytics.com https://stats.g.doubleclick.net https://www.facebook.com
Esto es lo que suele cortar el envío de eventos a las plataformas. El script de GTM se carga correctamente (gracias a script-src), el evento se dispara en GTM, pero cuando intenta enviar el hit a Google Analytics o al servidor de Meta, esa petición de red queda bloqueada por connect-src.
Este es el error más silencioso y confuso de todos: el Debug View de GTM muestra actividad, pero los informes están vacíos.
img-src
Controla la carga de imágenes, incluyendo píxeles de seguimiento de 1x1px (muy utilizados por Meta, Criteo, Pinterest, y muchas otras plataformas publicitarias).
img-src 'self' https://www.facebook.com https://www.google.com data:
El valor data: es necesario si usas imágenes codificadas en base64 directamente en el HTML. Sin el dominio de Meta aquí, el píxel de conversión de Facebook no funcionará aunque el script esté cargado.
Otras directivas relevantes
style-src — Controla las hojas de estilo. Menos crítica para el tracking puro, pero relevante si usas herramientas de experimentación (VWO, Optimizely) que inyectan estilos inline para ocultar el contenido original durante una prueba A/B.
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com
font-src — Controla la carga de fuentes externas.
font-src 'self' https://fonts.gstatic.com
frame-src y child-src — Controlan qué iframes pueden cargarse. Relevante si usas el modo de vista previa de GTM (que utiliza un iframe) o herramientas que cargan contenido embebido.
frame-src 'self' https://www.googletagmanager.com
worker-src — Controla si la web puede ejecutar procesos técnicos en segundo plano. No suele afectar a la analítica básica, pero puede ser necesario en implementaciones avanzadas de tracking o funcionalidades web más complejas.
La regla que cambia todo: ausencia de directiva ≠ «todo permitido» (si existe default-src)
Este es el punto que más confusión genera. Hay tres escenarios completamente distintos:
| Situación | Comportamiento |
|---|---|
| No hay CSP en absoluto | Todo permitido para todos los tipos de recursos |
| Hay CSP pero falta una directiva específica (y existe default-src) | Se aplica default-src como fallback |
| Hay CSP, falta directiva específica (y no existe default-src) | Todo permitido para ese tipo de recurso |
Dicho de otro modo: una CSP parcial puede ser más restrictiva de lo que parece si default-src ‘none’ actúa como candado general.
Por qué ahora: el endurecimiento de Chrome
La CSP lleva años existiendo, pero el impacto real en analítica web se ha acelerado en los últimos meses. Las versiones 120+ de Chrome han implementado un cumplimiento mucho más estricto de estas cabeceras.
Antes, los navegadores tendían a ser permisivos: bloqueaban recursos de forma silenciosa, toleraban configuraciones ambiguas, o simplemente dejaban pasar cosas que técnicamente deberían haber bloqueado. Hoy Chrome bloquea de forma inmediata y visible cualquier recurso que no cumpla la política. Los errores aparecen en consola con el prefijo Content Security Policy: en rojo.
El efecto práctico es que webs que «funcionaban» durante años con una CSP mal configurada han empezado a perder datos de golpe, sin que nadie haya tocado nada en la implementación de analítica.
Además, los navegadores no aplican la CSP de forma idéntica entre sí:
| Navegador | Nivel de aplicación CSP | Particularidades |
|---|---|---|
| Chrome 120+ | Muy estricto | Bloqueo agresivo, errores visibles en consola |
| Edge | Alto | Comportamiento similar a Chrome, con variaciones en privacidad |
| Safari | Alto + privacidad | ITP añade restricciones adicionales al tracking |
| Firefox | Alto | Errores más técnicos, menos visibles para el usuario |
Esto genera el escenario más difícil de diagnosticar: un evento funciona en Firefox pero no en Chrome, o viceversa.
Qué bloquea la CSP en la práctica: los tres conflictos más comunes
1. Scripts externos no autorizados
GTM carga desde https://www.googletagmanager.com. Si ese dominio no está en script-src, GTM directamente no se ejecuta. El resto de la cadena ni llega a arrancar.
# Sin esto, GTM no carga
script-src https://www.googletagmanager.com
2. Scripts inline (códigos scripts directamente en código fuente HTML)
El propio snippet de GTM e incluso los Custom HTML Tags de GTM son scripts inline: código JavaScript escrito directamente en el HTML, no cargado desde un archivo externo. Los reconoces dentro del código porque empiezan con tag html <script> y cierran con </script).
Por defecto, la CSP los bloquea. Para autorizarlos tienes tres opciones:
- unsafe-inline: autoriza todos los scripts inline. Fácil de implementar pero debilita la seguridad. Si queremos poder permitir las tags HTML personalizado de GTM, que hacen cosas con JS, hemos de lograr que esto se incluya en script-src.
- nonce: el servidor genera un token único por carga de página, lo añade tanto a la cabecera CSP como al atributo del script (ej. <script nonce=1324564674564«>). El navegador solo ejecuta los scripts cuyo nonce coincida, nonce es «number used once», un número aleatorio que ha de generarse desde el servidor y calculado dinámicamente desde la web con una variable. Método recomendado, pero el más complejo a la vez.
- hash: se calcula la huella SHA-256 del bloque de código y se añade a la CSP. Si el código cambia mínimamente, el hash cambia y el script se bloquea. Poco escalable. Desaconsejado en el tipo de implementaciones «vivas» con que solemos trabajar, ya que cada vez que cambie un ; del código o que añadamos cualquier servicio, hay que volver a calcularlo y pedir que nos cambien el código web. Lo mencionamos solo para advertir de que es un método que en principio vamos a descartar.
3. eval() y Variables de JavaScript Personalizado
Las Variables de JavaScript Personalizado de GTM utilizan internamente eval() o funciones equivalentes para ejecutar código dinámico. Sin unsafe-eval en script-src, estas variables devuelven undefined sin ningún mensaje de error claro.
script-src 'unsafe-eval'
Este es el bug más difícil de diagnosticar: los datos llegan, pero las dimensiones personalizadas están vacías o son incorrectas.
Por qué no puedes solucionar esto desde GTM
Una pregunta razonable: ¿puedo inyectar desde GTM una etiqueta que modifique la CSP?
No. Es imposible por diseño.
La CSP llega en la cabecera HTTP de la respuesta del servidor, que el navegador procesa antes de que cualquier JavaScript empiece a ejecutarse. Para cuando GTM corre, la política ya está activa y no puede ser modificada por scripts de la página. Es una capa de protección que existe precisamente para que no pueda ser anulada desde el propio documento.
La solución siempre tiene que venir del servidor: ya sea en la configuración de Apache/Nginx, en alguna capa intermedia de la aplicación, o en una etiqueta meta http-equiv=»Content-Security-Policy» en el head del HTML original (con limitaciones: la etiqueta meta no soporta todas las directivas y -además- si tenemos CSP en servidor + CSP en meta tag, se le da prioridad a la del servidor).
Cómo diagnosticar el problema sin tocar el servidor
Paso 1: Lee los errores de consola
Abre Chrome DevTools (F12), ve a la pestaña Consola y filtra por «CSP» o busca los errores en rojo que comienzan por Content Security Policy:. Cada error te indica exactamente qué recurso fue bloqueado y qué directiva lo ha denegado.
Ejemplo de error típico:
Content Security Policy: The page's settings blocked the loading of a resource at
https://www.facebook.com/tr/ ("img-src").
Esto te dice: el píxel de Meta (una imagen de tracking) está siendo bloqueado porque img-src no incluye https://www.facebook.com.
Paso 2: Usa IA para transformar los errores en directivas
Cuando la consola te da 30 errores de dominios distintos, el proceso manual es tedioso. Copia todos los errores y usa un modelo como Claude con este prompt:
«Actúa como experto en seguridad web y analítica. Aquí tienes los errores de CSP de la consola de Chrome al cargar mi web con GTM. Genera las directivas script-src, img-src y connect-src necesarias, organizadas por herramienta (GTM, GA4, Meta, etc.). Usa wildcards si es necesario para no exceder la longitud máxima de la CSP.»
El resultado es un borrador de directivas listo para pasarle a IT, sin necesidad de parsear errores a mano.
Paso 3: Valida si puedes tu propuesta con Local Overrides (comprueba el efecto sin tocar el servidor)
Antes de abrir un ticket a desarrollo que tarde semanas o meses, puedes probar si tu nueva CSP funciona directamente en tu navegador:
- Abre DevTools → pestaña Red (Network)
- Haz clic derecho sobre el documento principal → Anular encabezados (Override headers)
- Modifica el valor de Content-Security-Policy con tu propuesta
- Recarga la página
- Comprueba en el Debug View de GTM si las etiquetas se disparan correctamente
Si funciona, tienes la validación técnica bastante completa. Puedes llevar a IT no una hipótesis sino una solución probada.
Los tres métodos de autorización de scripts: cuándo usar cada uno
| Método | Seguridad | Escalabilidad | Cuándo usarlo |
|---|---|---|---|
| Nonce | Alta | Alta | Cuando el servidor puede generar tokens dinámicos. Opción recomendada. |
| Hash | Alta | Baja | Para scripts que nunca cambian. No escalable con GTM activo. |
| unsafe-inline | Baja | Alta | Último recurso si la plataforma no permite nonces. |
El truco del nonce con GTM: puedes crear una Variable de tipo «Elemento DOM» en GTM que capture el valor del nonce del snippet de GTM. Luego pasas esa variable como atributo nonce en todos tus Custom HTML Tags. De esta forma, cada etiqueta inline hereda automáticamente el nonce válido de esa sesión.
La CSP como sistema vivo, no como configuración puntual
El error más frecuente que cometen los equipos al configurar la CSP es tratarla como una tarea de una sola vez. Se configura, se olvida, y seis meses después alguien añade un nuevo píxel de conversión o una herramienta de heatmapping y los datos de esa herramienta nunca llegan porque su dominio no está en la política.
La CSP debe mantenerse igual que el propio trabajo de analítica: cada vez que se incorpora una nueva herramienta, hay que actualizar las directivas correspondientes. Idealmente, deberíais tener un proceso (aunque sea simple) para que cuando alguien solicite añadir una nueva herramienta de terceros, el equipo de IT actualice la CSP de forma coordinada.
Herramientas como Report-URI permiten configurar la CSP en modo report-only primero: la política no bloquea nada pero envía informes a un endpoint cada vez que algo hubiera sido bloqueado. Es una forma de auditar el impacto antes de activar la restricción real.
Checklist rápido para el equipo de analítica
Antes de escalar a IT, verifica:
-
- ¿La web tiene CSP activa? (Consola → Network → Headers de la respuesta principal)
- ¿Qué directivas están declaradas?
- ¿Hay errores CSP en consola al cargar la web?
- ¿Hay errores adicionales en las páginas y eventos de conversión (thank you page, envíos de formularios clave, etc.)?
- ¿El problema ocurre en Chrome pero no en otros navegadores?
- ¿Has recopilado todos los dominios bloqueados por herramienta?
- ¿Tienes una propuesta de directivas validada con Local Overrides?
Recursos para profundizar
- ¿Qué es la CSP y por qué nos afecta en analítica web?: introducción y estructura base de una CSP para un correcto tracking web
- MDN Web Docs — Content Security Policy: referencia técnica completa
- CSP Evaluator de Google: analiza la seguridad de tu política actual
- Report-URI: monitorización de violaciones CSP en tiempo real
- Guía CSP para GTM — Google Developers: configuración específica para GTM con nonces
- Security Headers: auditoría de todas las cabeceras HTTP de seguridad de tu web
- Chrome DevTools — Local Overrides: tutorial oficial para testear cabeceras sin modificar el servidor
La seguridad web y la medición digital ya no son silos independientes. Cuanto antes lo integréis en vuestros flujos de trabajo, menos datos perderéis en el camino.
