Por que as capturas de tela dos seus heatmaps do Matomo estão quebradas (e como corrigir)

As capturas de tela dos heatmaps do Matomo quebram porque sua página é re-renderizada em um servidor diferente, sem cookies, fontes ou acesso CORS. Veja o que causa cada problema e como resolver.

A captura de tela do Matomo funciona em duas etapas, e o problema mora justamente no intervalo entre elas.

Na primeira etapa, quando alguém acessa sua página, o JavaScript de rastreamento do Matomo serializa o DOM numa string HTML (usando uma biblioteca chamada TreeMirror) e manda pro seu servidor Matomo. Na segunda, o servidor do Matomo renderiza esse HTML pra gerar a imagem de fundo que aparece na visualização do heatmap.

Aí é que a coisa complica: a página está sendo re-renderizada num contexto totalmente diferente. O servidor do Matomo não tem cookies, não tem sessões, a origem é outra, e ele não faz a menor ideia do que seus frameworks JavaScript fizeram na página depois do carregamento. Tudo que funcionava porque estava rodando num navegador real, com as credenciais do seu domínio, pode simplesmente quebrar quando o mesmo HTML é renderizado a frio num servidor diferente.

Quando você entende esse mecanismo, a maioria dos bugs de captura de tela dos heatmaps passa a fazer sentido.

Como ficam as capturas de tela quebradas

Esses são os problemas que aparecem com mais frequência:

  • A imagem hero carrega normal no navegador mas aparece como ícone quebrado no heatmap
  • Uma grade de produtos com scroll mostra só a primeira linha, o resto é cortado
  • A navegação fixa fica por cima do conteúdo da página na captura
  • As fontes da marca somem e aparecem fontes genéricas do sistema no lugar
  • Assets com caminhos relativos tipo /assets/banner.webp simplesmente não carregam
  • Páginas SPA capturadas no meio da renderização, com metade do conteúdo faltando

Cada um desses tem uma causa técnica específica. Vamos ver caso a caso.

CORS bloqueando imagens e fontes

Esse é o problema mais comum de longe. Sua imagem hero, fotos de produtos ou imagens de fundo aparecem como caixas vazias ou ícones quebrados na captura do heatmap.

O que acontece: suas imagens carregam sem problema no navegador, porque o navegador tem seus cookies e o contexto da sessão. Mas quando o servidor do Matomo tenta carregar https://cdn.yoursite.com/hero.jpg, o CDN vê uma origem que não conhece e recusa a requisição. CORS, basicamente.

No lado do servidor, a correção é simples: adicionar Access-Control-Allow-Origin: * nos cabeçalhos de resposta do CDN. A documentação do Matomo cobre isso no guia de configuração de heatmaps.

Se você não tem controle sobre o CDN ou não quer mexer na configuração do servidor, a alternativa é embutir os recursos direto no DOM como data URIs em base64 antes do Matomo capturar a página. Assim o HTML serializado já carrega cada imagem inline e o servidor nunca precisa buscar nada externamente. Isso vale pra src, srcset, imagens de fundo CSS, referências SVG e imagens de poster de vídeo.

Containers com scroll cortando conteúdo

Você tem uma seção com altura fixa e barra de rolagem. Na captura do heatmap, só a parte visível aparece. Tudo que ficava abaixo da dobra desaparece.

Elementos com overflow: hidden ou overflow: auto cortam o conteúdo na altura renderizada. O Matomo serializa o DOM no estado visual atual, então o HTML capturado reflete o clientHeight, não o scrollHeight completo.

Pra resolver: evite alturas fixas em containers de conteúdo. Use min-height no lugar de height, e não coloque overflow: hidden em elementos que têm conteúdo rolável.

Headers fixos e sticky por cima da página

Sua navegação sticky funciona perfeitamente no navegador, mas na captura ela simplesmente senta em cima do conteúdo e cobre o que tem por baixo.

Elementos com position: fixed e position: sticky são posicionados em relação ao viewport. No servidor, não existe viewport, e eles acabam colapsando sobre o conteúdo.

O Matomo adiciona a classe matomoHeatmap no elemento <html> durante a renderização da captura no servidor. Dá pra usar isso pra escrever regras CSS que só se aplicam na hora da captura:

css
html.matomoHeatmap .your-sticky-header {
  position: relative;
  top: auto;
}

Isso converte o header sticky pro fluxo normal na captura, sem afetar nada pra quem está navegando. A documentação do Matomo fala sobre isso no FAQ de headers sobrepostos em heatmaps e de stylesheets personalizadas durante a captura.

Se você não quiser manter essas regras CSS extras, a abordagem geral é detectar elementos de header e nav com posicionamento fixed ou sticky, converter pra position: relative, zerar os valores de top/bottom/left/right e, no caso de elementos fixed, colocar um div placeholder pra evitar que o layout pule. Depois da captura, tudo volta ao normal.

URLs relativas que param de funcionar

Algumas imagens e stylesheets carregam normal no seu site mas somem na captura do Matomo, mesmo não sendo cross-origin. Parece estranho até você entender o que está rolando.

Sua página usa URLs relativas tipo /images/banner.jpg. No navegador, isso resolve pro seu domínio. No contexto de renderização do Matomo, /images/banner.jpg resolve pro servidor do Matomo. O arquivo não existe lá.

A solução: use URLs absolutas em todo lugar, ou adicione uma tag <base href="https://yoursite.com/"> no <head>. Se nenhuma dessas opções for viável, um script de pré-captura pode varrer o documento procurando URLs relativas em atributos src, href, srcset e background-image no CSS, e reescrever tudo pra URLs absolutas usando a origem atual da página.

Fontes customizadas aparecendo como padrão do sistema

Suas fontes da marca sumiram. A captura mostra a fonte do sistema que o servidor tiver disponível, que geralmente é algo bem feio.

As regras @font-face referenciam arquivos de fonte hospedados em CDNs ou no seu servidor. Quando o servidor do Matomo tenta carregar esses arquivos, leva um bloqueio de CORS ou o CDN exige um user agent de navegador. O servidor acaba usando a fonte padrão.

Pra resolver: hospede as fontes num servidor com cabeçalhos CORS permissivos. O Google Fonts já faz isso por padrão, mas muitas fontes auto-hospedadas não. Se mexer nos cabeçalhos não for uma opção, dá pra embutir as fontes direto na página buscando os arquivos, convertendo pra data URIs em base64 e injetando regras @font-face com esses data URIs em tags <style> no <head>. O HTML serializado passa a carregar suas próprias definições de fonte e renderiza certo independente de onde estiver sendo servido.

Iframes, vídeos e SPAs

Mais alguns casos que vale conhecer.

Iframes costumam ter dimensões CSS fixas que não batem com o conteúdo real. Pra iframes de mesma origem, dá pra ler a altura real do conteúdo via contentDocument.body.scrollHeight e redimensionar. Pra iframes cross-origin, vai precisar de uma altura de fallback generosa.

Vídeos com autoplay são capturados durante a reprodução e mostram um frame qualquer. Pause e faça seek pro frame 0 antes da captura pra que a imagem mostre o primeiro frame.

Single page applications são o caso mais chato. Apps React, Vue e Angular carregam conteúdo dinamicamente, e o Matomo pode capturar o DOM antes do framework terminar de renderizar. A URL pode não bater com o que o router está exibindo também. Não tem bala de prata. O melhor caminho é configurar as regras de correspondência de URL do Matomo pras suas rotas SPA e usar a API JavaScript do Matomo pra disparar a gravação depois que a renderização terminar.

Corrigindo tudo de uma vez com o Matomo Heatmap Helper

Na prática, a maioria dos sites tem vários desses problemas ao mesmo tempo. Resolver tudo na mão significa mexer em configuração de CDN, reescrever URLs, refatorar CSS e escrever scripts de captura customizados.

O Matomo Heatmap Helper é uma extensão gratuita e open source pra Chrome e Firefox que lida com todos esses problemas. Código-fonte no GitHub.

Na prática funciona assim: você instala a extensão, coloca sua URL do Matomo e token de API, e escolhe quais sites ativar. Nas páginas correspondentes, aparece uma barra de ferramentas. Clica em "Interactive" e vai clicando nos elementos que precisam de correção: containers com scroll, headers sticky, seções com imagens faltando. Cada um recebe um ícone de cadeado indicando que vai ser processado.

Quando você aperta o botão de captura, a extensão roda uma sequência. Primeiro busca e embute imagens e fontes cross-origin, depois expande os elementos restritos e tira o posicionamento sticky dos headers, trata iframes e vídeos, converte URLs relativas pra absolutas e dispara a API de captura do Matomo. Depois da captura, cada alteração é revertida e a página volta ao estado original.

Antes de tirar a captura

Checklist rápido:

  1. Confira se a página renderizou por completo, principalmente conteúdo SPA e seções com carregamento lazy
  2. Trave containers que cortam conteúdo ou que precisam de expansão
  3. Veja se tem algum header sticky ou fixed que possa sobrepor o conteúdo
  4. Confirme que suas imagens e fontes principais estão aparecendo
  5. Tire a captura e confirme que a verificação passou
  6. Abra a visualização do heatmap e confira o resultado antes de começar a análise

Dois caminhos

Esses problemas de captura de tela não são bugs do seu site. São efeitos colaterais de o Matomo serializar a página e re-renderizar em outro lugar. Qualquer site que use CDNs, web fonts, containers com scroll ou headers sticky vai esbarrar em alguma combinação deles.

Dá pra corrigir cada problema no nível de infraestrutura: adicionar cabeçalhos CORS, usar URLs absolutas, refatorar containers com overflow, alternar o posicionamento sticky durante a captura. Essa é a abordagem certa a longo prazo se você tem controle sobre o servidor, mas leva tempo e alguns problemas não têm solução limpa no lado do servidor.

Ou dá pra resolver na hora da captura com o Matomo Heatmap Helper. Use o modo interativo pra indicar quais partes da página precisam de ajuda e tenha capturas de tela limpas sem mexer no código de produção.