Como o dado viaja
Cada clique passa por cinco camadas antes de virar dado analisável. O consentimento do visitante é checado antes de qualquer cookie ser gravado.
Site
Elementos marcados com data-track e um script que envia ao dataLayer
Consentimento
Padrão negado. Banner próprio atualiza o Consent Mode v2
GTM web
Acionadores por evento e uma tag GA4 para cada um
GTM server
Stape em São Paulo, no subdomínio próprio sv.
GA4 + BigQuery
Relatórios, conversões e exportação diária dos eventos brutos
O navegador não fala direto com o Google: as requisições vão para sv.portfoliovinicius.com.br, um servidor sob o meu domínio, que repassa os eventos ao GA4.
GTM web (client-side)
Roda no navegador do visitante. É quem enxerga o que acontece na página.
- Captura as interações: cliques, downloads, envio de formulário. O servidor não vê a página, então essa parte só pode acontecer no navegador.
- Respeita o consentimento na origem: lê o Consent Mode antes de gravar qualquer cookie ou enviar dados.
- Monta o evento: transforma o dataLayer em eventos padronizados, com os parâmetros certos.
GTM server (server-side)
Roda num servidor sob o meu domínio. É quem decide o que sai e para onde.
- Coleta first-party: o navegador envia para
sv.portfoliovinicius.com.br, e não para um domínio do Google, o que reduz perdas por bloqueadores e restrições de navegador. - Controle do dado: é possível remover IP, dados pessoais ou parâmetros sensíveis antes de qualquer envio a terceiros.
- Um evento, vários destinos: o mesmo evento pode ir para GA4, Meta e Google Ads sem carregar um script de cada plataforma na página, o que deixa o site mais leve.
Por que os dois juntos
Um não substitui o outro. O client-side sabe o que o visitante fez; o server-side controla como e para onde esse dado vai. Integrados, cada um cobre a limitação do outro:
O que é medido e por quê
O objetivo do site é simples: o visitante conhecer os projetos e entrar em contato. Os eventos foram escolhidos a partir disso, antes de abrir qualquer ferramenta.
| Evento | Quando dispara | Parâmetros | Conversão |
|---|---|---|---|
page_view | Toda página carregada | page_location, page_title | Não |
view_project_section | Clique em "Ver Mais" ou "Ver Código" nos cards da home | section_name | Não |
select_project | Clique em "Ver Dashboard" de um projeto | project_name, project_type (web / powerbi) | Não |
file_download | Download do currículo (3 botões) | file_name, link_url | Sim |
contact_click | Clique no LinkedIn | contact_method | Sim |
generate_lead | Formulário "Fale Comigo" enviado com sucesso | contact_method = formulario | Sim |
Passo a passo
Cada etapa gerou evidências. Clique nas imagens para ampliar.
Propriedade com fuso de São Paulo e moeda em reais, retenção de dados ampliada para 14 meses e o GA4 carregado só pelo GTM, nunca direto no HTML.
- Medição aprimorada ajustada: cliques de saída, downloads e interações com formulário desligados, porque esses eventos são tratados pelo GTM com mais controle. Sem isso, cada download seria contado duas vezes.
- Dimensões personalizadas para section_name, project_name, project_type e contact_method, para os parâmetros aparecerem nos relatórios.
- Tráfego interno marcado por IP e excluído por filtro de dados, para meus próprios testes não virarem conversões falsas.
- Eventos principais: file_download, contact_click e generate_lead, sem valor monetário (não há receita num portfólio).
/static/img/tracking/ga4-medicao-aprimorada.png
/static/img/tracking/ga4-dimensoes.png
/static/img/tracking/ga4-eventos-principais.pngEm vez de um acionador de clique por elemento, cada link relevante recebe atributos data-, e um único listener envia tudo para o dataLayer. Adicionar um botão novo ao tracking é só colocar um atributo no HTML.
HTML
<a href="/notebooks-dashboard/" data-track="select_project" data-project="radar-precos-notebooks" data-type="web">Ver Dashboard</a>
static/js/tracking.js
document.addEventListener('click', function (e) { var el = e.target.closest('[data-track]'); if (!el) return; var payload = { event: el.dataset.track, section_name: el.dataset.section, project_name: el.dataset.project, project_type: el.dataset.type, file_name: el.dataset.file, contact_method: el.dataset.method, link_url: el.href }; // Remove parâmetros vazios para não mandar "undefined" ao GA4 Object.keys(payload).forEach(function (k) { if (payload[k] === undefined || payload[k] === '') delete payload[k]; }); window.dataLayer.push(payload); });
No container, um padrão de nomes deixa tudo legível: GA4 - Evento - contact_click, CE - contact_click (acionador de evento personalizado), DLV - project_name (variável da camada de dados), separados em pastas.
/static/img/tracking/gtm-tags.png
/static/img/tracking/gtm-tag-contact-click.png
/static/img/tracking/gtm-versoes.pngO site começa com todos os consentimentos negados. O banner é próprio, em HTML e JS, com "Aceitar" e "Recusar" do mesmo tamanho, e a escolha fica salva no navegador. Quem já escolheu antes tem o consentimento atualizado antes mesmo de o GTM carregar.
No <head>, antes do GTM
gtag('consent', 'default', { analytics_storage: 'denied', ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', wait_for_update: 500 }); // Visitante que já aceitou: libera antes do GTM carregar if (localStorage.getItem('consent_analytics') === 'granted') { gtag('consent', 'update', { analytics_storage: 'granted' }); }
Quem recusa não recebe cookies de análise. O GA4 recebe apenas sinais anônimos, usados para modelagem (modo avançado do Consent Mode). Os consentimentos de anúncio ficam sempre negados, porque o site não faz publicidade.
/static/img/tracking/consent-banner.png
/static/img/tracking/consent-tag-assistant.png
/static/img/tracking/consent-visao-geral.pngUm segundo container, do tipo servidor, roda na Stape em São Paulo. O container web passou a enviar os eventos para sv.portfoliovinicius.com.br (parâmetro server_container_url na tag do Google), e o servidor repassa ao GA4.
- Domínio próprio: registro CNAME na Hostinger apontando para a Stape. A coleta passa a ser primária (first-party), o que é mais resistente a bloqueios.
- Nome neutro de propósito: o subdomínio é
sv, e nãometricsouanalytics, que costumam estar nas listas de bloqueadores de anúncio. - No servidor: o cliente GA4 recebe a requisição, e uma tag GA4 com acionador
Client Name = GA4repassa ao Google. A regra de tráfego interno continuou funcionando depois da mudança.
/static/img/tracking/server-devtools-collect.png
/static/img/tracking/server-preview.png
/static/img/tracking/server-stape-dominio.pngO GA4 exporta diariamente os eventos brutos para o BigQuery (região São Paulo). É o que permite cruzar o dado do site com outras fontes e responder perguntas que a interface do GA4 não responde.
Exemplo: cliques por projeto, direto da tabela exportada
SELECT (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'project_name') AS projeto, (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'project_type') AS tipo, COUNT(*) AS cliques, COUNT(DISTINCT user_pseudo_id) AS usuarios FROM `analytics_556859069.events_*` WHERE event_name = 'select_project' GROUP BY projeto, tipo ORDER BY cliques DESC;
/static/img/tracking/bq-vinculo.png
/static/img/tracking/bq-tabelas.pngFunciona, não só está configurado
Toda etapa foi testada no modo de visualização do GTM e no DebugView do GA4 antes de ser publicada.
Os 6 eventos disparando no Tag Assistant e chegando ao DebugView com os parâmetros preenchidos.
Padrão negado, atualização para concedido ao aceitar e escolha respeitada ao recarregar.
Requisições saindo para o subdomínio próprio e o servidor confirmando o envio ao GA4.
Meus acessos marcados como internos mesmo depois da passagem pelo servidor.
/static/img/tracking/validacao-tag-assistant.png
/static/img/tracking/validacao-debugview.pngPor que foi feito assim
Uma tag por evento, não uma genérica
O dataLayer guarda valores de pushes anteriores. Com uma tag única usando {{Event}}, um contact_click podia herdar o project_name de um clique anterior. Com uma tag por evento, cada uma só mapeia o que é dela.
Atributos data- em vez de acionadores de clique
O tracking fica declarado no próprio HTML, fácil de revisar, e não quebra quando a classe ou o texto de um botão muda.
generate_lead só no sucesso
O evento dispara na resposta positiva do envio, e não no clique em "Enviar". Um formulário com erro não vira conversão.
Banner próprio em vez de plataforma pronta
Controle total do visual e do comportamento, e a prova de que entendo o Consent Mode por dentro, e não só uma ferramenta que o configura.
Stape em vez da hospedagem do site
O GTM server é um programa Node.js (imagem Docker do Google) que precisa ficar rodando o tempo todo, e a hospedagem compartilhada só executa PHP sob demanda. Entre Stape, Google Cloud Run e uma VPS própria, a Stape resolve domínio, SSL e região em São Paulo sem manutenção e com plano gratuito.
O servidor vira uma dependência
Com server-side, o GA4 só recebe o que passa pelo servidor: se ele parar ou a cota acabar, os eventos se perdem. Por isso o uso é acompanhado, e a contingência é simples: remover o server_container_url da tag do Google faz o navegador voltar a enviar direto ao Google.
- Meta Conversions API e conversões otimizadas do Google Ads a partir do mesmo container server.
- Enriquecimento de eventos no servidor (dados de CRM) e remoção de dados pessoais antes do envio.
- Painel no Looker Studio ou Power BI sobre a exportação do BigQuery, cruzando tráfego com mídia e vendas.