Server-side GTM en Cloud Run con tu propio dominio: el montaje y el fallo que no sale en ningún log
Monté un servidor de tagging en Google Cloud Run, con mi propio dominio y sin pasar por un proveedor gestionado. Esto es el montaje en orden, lo que decidí en cada paso y el fallo que me costó más tiempo, que no era ninguno de los que esperaba.
Qué monto
Un contenedor de GTM de tipo servidor, desplegado en Cloud Run, accesible en sgtm.tudominio.com. El navegador envía los hits de GA4 a ese subdominio propio en lugar de a google-analytics.com, y el servidor los reenvía a GA4.
Los pasos, en el orden que importa
1. Aprovisionamiento desde GTM. Al crear el contenedor de servidor, GTM puede aprovisionar el servicio en Cloud Run automáticamente. Crea el proyecto de Google Cloud por ti. Mi proyecto quedó con un nombre autogenerado y es el que uso; no hace falta tener uno propio antes.
2. Región. Elegí us-central1, la región con free tier de egreso. Está lejos de España, pero la latencia no importa: el navegador manda un beacon asíncrono y no espera la respuesta para pintar nada.
3. Facturación y escalado. Facturación por solicitud (cpu-throttling activado), mínimo de instancias en 0 y máximo en 1. Con esa combinación el servicio no cobra mientras nadie lo usa y no puede crecer sin control. Añade una alerta de presupuesto desde el primer día; la mía es de 5 € con avisos al 50, 90 y 100 %.
4. Dominio propio. Cloud Run permite asignar un dominio al servicio. Necesitas tener el dominio verificado en Search Console como propiedad de Dominio. Después crea un CNAME sgtm que apunte a ghs.googlehosted.com. Si tu DNS está en Cloudflare, déjalo en solo DNS (sin proxy), como hice yo, para que Google pueda validar el dominio y emitir el certificado él mismo.
5. Certificado. Google lo emite solo. La documentación habla de hasta 24 horas; a mí me tardó aproximadamente una hora. No toques nada mientras tanto.
6. GTM web. En la etiqueta de Google, añade server_container_url con tu subdominio y first_party_collection en true.
7. GTM servidor. Un cliente GA4 (con las rutas predeterminadas) y una etiqueta GA4 que dispare con todos los eventos del cliente. Publica ambos contenedores.
Hasta aquí es lo que cuentan las guías. En mi caso todo estaba en verde y no llegaba ni un hit real.
El fallo: la petición nunca salía del navegador
Las pruebas directas contra el servidor devolvían 200. gtm.js cargaba con 200. El contenedor estaba publicado. Y en los logs de Cloud Run no aparecía tráfico de visitantes, solo mis pruebas manuales.
La causa no estaba en el servidor, y por eso no aparecía en sus logs. El Content-Security-Policy de mi web no incluía https://sgtm.tudominio.com en connect-src. El navegador bloqueaba el fetch hacia /g/collect antes de enviarlo. Desde el servidor, esa petición simplemente no existe. El error solo estaba en la consola del navegador:
Refused to connect to 'https://sgtm.tudominio.com/g/collect…'
because it violates the document's Content Security Policy.
Es un fallo especialmente traicionero porque gtm.js sí carga (viene de googletagmanager.com, que sueles tener permitido) y parece que todo funciona. La solución es una línea: añadir tu subdominio a connect-src.
Cómo verificar que de verdad funciona
Una prueba sintética contra la API no demuestra nada. Para dar el montaje por bueno:
- Abre tu web en una ventana nueva con DevTools abierto y la consola visible.
- Comprueba que no hay errores de CSP y que aparece una petición a
sgtm.tudominio.com/g/collectcon estado 200. - Mira los logs de Cloud Run: debe aparecer la misma petición, con tu web como referer.
- Confirma el evento en GA4 → Tiempo real.
Si falta cualquiera de los cuatro, el montaje no está terminado.
Qué vigilar después
- Que el mínimo de instancias siga en 0. Una instancia siempre encendida cuesta decenas de euros al mes; es la forma más fácil de convertir un montaje gratuito en uno de pago sin darte cuenta.
- Que el máximo de instancias siga capado, para que un pico anómalo no dispare la factura.
- La alerta de presupuesto, que es lo único que te avisa si algo de lo anterior cambia.
Lo documenté como caso con el contexto completo: servidor de tagging propio en Cloud Run.