Si alguna vez has abierto un heatmap de Matomo y te has encontrado un rectángulo en blanco donde debería estar tu imagen hero, una cuadrícula de productos que se corta después de la primera fila o una cabecera sticky plantada encima del contenido de la página, no eres el primero. Los heatmaps se rompen en la mayoría de sitios web modernos sin hacer nada especial. La causa es estructural, no algo que se arregle tocando la configuración del seguimiento.
El script de seguimiento de Matomo serializa el DOM de tu página en HTML y lo manda a tu servidor de Matomo. El servidor vuelve a renderizar ese HTML en un contexto completamente distinto para generar la captura que ves. Sin cookies. Sin sesión. Origen diferente. Sin el runtime que montó tu framework de JavaScript. Muchas cosas que funcionaban porque corrían en un navegador real fallan en silencio cuando ese mismo HTML se vuelve a renderizar en frío en otro sitio.
Una vez lo entiendes, la lista de bugs empieza a tener sentido.
Los patrones que nos seguimos encontrando
La mayoría de sitios tienen al menos tres de estos. Algunos tienen los seis.
- CORS bloquea tus imágenes y fuentes alojadas en CDN, porque el servidor de Matomo no tiene credenciales de tu dominio. Las imágenes aparecen como iconos rotos. Las fuentes caen a las del sistema.
- Los contenedores con scroll (
overflow: hiddenooverflow: autocon altura fija) cortan todo lo que está por debajo de su área visible. Matomo serializa el DOM en su estado actual, así que lo que no estaba en pantalla en el momento de la captura no aparece en el snapshot. - Las cabeceras sticky y fixed se colapsan sobre el contenido que tienen debajo. Su posición se calcula respecto a un viewport, y en el servidor no hay viewport, así que caen donde les toca en el DOM.
- URLs relativas como
/images/hero.jpgse resuelven contra el servidor de Matomo en lugar del tuyo. El archivo no existe allí, así que simplemente falla en silencio. - Las fuentes personalizadas cargadas con
@font-facecaen a las del sistema cuando los archivos de fuente están protegidos por CORS o bloquean el user-agent del servidor de Matomo. - Las single-page apps se capturan antes de que React o Vue hayan terminado de renderizar, dejando media página sin aparecer en el snapshot.
La mayoría de estos tienen soluciones a nivel de infraestructura. Añade cabeceras CORS en tu CDN. Pasa a URLs absolutas. Usa un hook CSS matomoHeatmap para sobrescribir el posicionamiento sticky durante la captura. La documentación de Matomo cubre varios de ellos, y nosotros hemos escrito un artículo más largo que repasa cada problema y cómo solucionarlo en el servidor si eso es lo que quieres hacer.
El problema es que el trabajo se reparte entre la configuración del CDN, el CSS y el tracking setup, y parte de él no puedes arreglarlo si no controlas los assets. Nos topamos con esa pared en suficientes sitios de clientes como para acabar haciendo algo diferente.
Lo que terminamos construyendo
Empezamos con un pequeño script que pegábamos en DevTools antes de cada captura. Incrustaba imágenes y fuentes como data URIs, expandía los contenedores con scroll, desanclaba las cabeceras y reescribía las URLs relativas a absolutas. Con el tiempo creció hasta convertirse en una extensión de Chrome que usábamos internamente en cada cliente.
Eso es Matomo Heatmap Helper. La hemos publicado como código abierto.
La configuración es una sola pantalla. Introduces tu URL de Matomo, un token de API y eliges en qué sitios quieres que aparezca la barra de herramientas. En cualquier página que coincida, aparece una pequeña barra.
El trabajo ocurre en el momento de la captura. Cuando pulsas el botón de screenshot, la extensión hace las cosas que son costosas de resolver de forma estática. Incrusta imágenes y fuentes como data URIs en base64 para que el servidor de Matomo nunca tenga que pedir nada de otro origen. Expande los contenedores con scroll a su altura de contenido completa. Convierte las cabeceras sticky y fixed a flujo normal. Reescribe URLs relativas a absolutas, redimensiona iframes, pausa vídeos en el fotograma cero y da tiempo al contenido de SPAs para renderizarse. Luego llama a la API de capturas de Matomo, espera el resultado y revierte todos los cambios. Tu página vuelve a estar como estaba.
La razón por la que esto funciona es que puedes interactuar con los elementos antes de capturar. Contenedores con scroll problemáticos, una cabecera sticky que no se porta bien, una sección donde los assets no cargan: haces clic en ellos en el modo interactivo, les aparece un icono de candado y el pipeline sabe qué hacer con ellos. El script de seguimiento solo no puede hacer eso, porque captura el estado en que estaba el DOM en el momento en que se ejecutó. Que una persona le diga a la extensión qué elementos necesitan atención es lo que resuelve los casos que el script nunca podría.
La extensión solo habla con tu instancia de Matomo. Tu token de API se guarda en chrome.storage.local, que está aislado de las páginas que visitas. Sin telemetría, sin seguimiento de uso. El código está en GitHub. Issues y pull requests son bienvenidos.
Por qué la publicamos
Como agencia Official Matomo Partner, los heatmaps rotos eran un problema con el que nos chocábamos en casi cada proyecto de cliente. Los eventos se registraban correctamente, pero las capturas eran inutilizables, que es la parte en la que la mayoría de stakeholders se fijan.
Matomo nos ha dado mucho como agencia. Usamos la extensión a diario en proyectos de clientes y seguimos mejorando los casos extremos que vamos encontrando en sitios web. Si te encuentras algún problema de heatmap sin resolver, abre un issue en GitHub y veremos si podemos añadir un fix a la extensión.
En qué estamos trabajando
Matomo Heatmap Helper surgió de un proyecto más grande. Martez conecta Matomo con Meta Ads y Google Ads, para que el ROAS, el CLV y la atribución multicanal estén junto a tu analítica web en lugar de en una hoja de cálculo aparte. Está en beta privada. Si eso te resulta relevante, puedes unirte a la lista de espera.
En cualquier caso, la extensión de heatmaps es su propia cosa y así seguirá siendo. La construimos porque la necesitábamos.