GA4 server-side com Measurement Protocol: enviando eventos direto do seu servidor
Guia técnico do Measurement Protocol do GA4: quando enviar eventos pelo servidor, como montar o payload, evitar duplicação e depurar o envio.
Neste artigo
O Google Analytics 4 nasceu com uma suposição embutida: os eventos chegam do navegador, via a biblioteca gtag.js, com um cookie de cliente para amarrar tudo a uma mesma sessão. Essa suposição funciona bem até o momento em que o evento que mais importa — a compra confirmada, o pagamento aprovado, o reembolso — acontece longe do navegador. Um pagamento é confirmado por webhook do gateway. Um pedido é validado por um job assíncrono. Uma assinatura renova sem ninguém abrir uma aba. Para todos esses casos, o GA4 oferece o Measurement Protocol: uma forma de enviar eventos diretamente do seu servidor, por HTTP, sem depender do navegador estar presente.
Este artigo explica quando o Measurement Protocol faz sentido, como montar uma requisição correta, como conectá-la aos eventos que o navegador já enviou sem duplicar dados, e como depurar quando o evento não aparece.
O que é o Measurement Protocol#
O Measurement Protocol do GA4 é um endpoint HTTP que aceita eventos em formato JSON. Você faz um POST com um payload descrevendo o evento, autenticado por uma chave de API (api_secret) associada ao seu fluxo de dados, e o GA4 processa aquele evento como se tivesse vindo de qualquer outra fonte.
É importante entender o que ele não é. Ele não é um substituto completo do gtag.js: não coleta automaticamente informações de tela, dispositivo, campanha ou geografia como o navegador faz. Ele é um canal para você enviar, explicitamente, os eventos que só o seu back-end conhece. Na prática, a maioria das implementações maduras combina os dois: o navegador cobre a navegação e o engajamento; o servidor cobre os eventos de valor que precisam ser confiáveis.
Quando usar o servidor em vez do navegador#
Nem todo evento deve migrar para o servidor. A regra prática é enviar pelo servidor aquilo que precisa ser verdadeiro e confiável, e deixar no navegador aquilo que é sobre comportamento e contexto.
Casos em que o servidor ganha:
- Conversões de valor confirmadas. Uma
purchasesó é real depois que o pagamento é aprovado. Disparar do navegador no clique de "finalizar" conta compras que falharam, foram recusadas ou abandonadas na tela do banco. - Eventos que acontecem sem navegador. Renovação de assinatura, aprovação de pagamento por boleto/Pix horas depois, mudança de status de pedido, reembolso.
- Ambientes onde o script não roda. Bloqueadores, restrições de privacidade e falhas de rede fazem uma fração dos eventos client-side nunca chegar. O servidor não é bloqueado.
- Correção e enriquecimento. Anexar dados que o navegador não tem — margem, categoria de cliente, LTV estimado — no momento certo.
Casos em que o navegador continua sendo a fonte natural: page_view, scroll, session_start, cliques em elementos, vídeo, tudo que é sobre a interação em si.
O identificador que amarra tudo: client_id#
O GA4 organiza sessões e usuários em torno do client_id, um identificador que o gtag.js gera e persiste no cookie _ga no navegador. Quando você envia um evento pelo servidor, o GA4 precisa saber a qual cliente aquele evento pertence. Se você inventar um client_id novo no servidor, o GA4 vai tratar aquilo como um usuário separado, e sua compra vai aparecer desconectada de toda a navegação que a precedeu.
A implicação é a mesma dos click IDs de anúncio: você precisa capturar o client_id do navegador enquanto o usuário está no site e carregá-lo junto do pedido até o back-end. O valor está no cookie _ga, no formato GA1.1.XXXXXXXXX.YYYYYYYYY; o client_id é a parte final (XXXXXXXXX.YYYYYYYYY).
``javascript // Lê o client_id do cookie _ga para enviar junto do pedido. function lerClientId() { const cookie = document.cookie .split("; ") .find((c) => c.startsWith("_ga=")); if (!cookie) return null; // _ga=GA1.1.1234567890.1699990000 -> "1234567890.1699990000" const partes = cookie.split("."); return partes.length >= 4 ? ${partes[2]}.${partes[3]} : null; } ``
Esse valor viaja em um campo oculto do checkout, é salvo com o pedido, e o back-end o usa quando dispara o evento server-side. Se você trabalha também com user_id (identificador logado próprio), envie os dois: o user_id melhora a análise cross-device, o client_id mantém a continuidade da sessão.
A estrutura do payload#
Uma requisição do Measurement Protocol é um POST para o endpoint de coleta, com dois parâmetros de identificação na query string (measurement_id e api_secret) e o evento no corpo. O corpo tem um client_id no topo e uma lista de events, cada um com name e params.
``json { "client_id": "1234567890.1699990000", "user_id": "user_8842", "events": [ { "name": "purchase", "params": { "transaction_id": "PED-2026-000123", "value": 349.90, "currency": "BRL", "items": [ { "item_id": "SKU-42", "item_name": "Tênis Trilha", "price": 349.90, "quantity": 1 } ], "session_id": "1699990000", "engagement_time_msec": 100 } } ] } ``
Alguns detalhes que fazem diferença:
transaction_idé a chave de deduplicação de comércio do GA4. Enviar a mesmapurchasecom o mesmotransaction_idevita contar a receita duas vezes.currencyé obrigatório sempre que houvervaluemonetário. Sem ele, a receita não é processada corretamente.session_ideengagement_time_msecajudam o GA4 a associar o evento a uma sessão existente e a não descartá-lo por parecer sem engajamento. Idealmente osession_idtambém é capturado do navegador.- Nomes de eventos e parâmetros seguem as convenções do GA4. Eventos recomendados (
purchase,sign_up,generate_lead) têm semântica reconhecida em relatórios; eventos custom precisam ser registrados como dimensões/métricas para aparecer.
O api_secret é uma credencial. Ele fica no servidor, nunca no bundle do navegador, nunca em variável exposta ao cliente. É exatamente o tipo de segredo que não pode vazar para o front-end.
Evitando a duplicação com o client-side#
O risco clássico ao adotar o servidor é contar a mesma conversão duas vezes: o navegador dispara purchase no clique de finalizar, e o servidor dispara purchase de novo quando o pagamento confirma. A abordagem correta é escolher uma única fonte de verdade por evento.
Para conversões de valor, a recomendação é deixar o servidor ser a fonte e remover o disparo client-side daquele evento específico. Assim você conta apenas compras reais. Se, por qualquer razão, os dois lados precisam disparar, use o mesmo transaction_id nos dois — o GA4 deduplica compras pela transação. Mas confiar na deduplicação como muleta para uma arquitetura confusa costuma sair caro; é mais limpo definir, evento a evento, quem é o dono.
Consentimento e dados do usuário#
Enviar do servidor não isenta você das regras de consentimento. Se o usuário não consentiu com analytics, o evento não deve ser enviado — mover a coleta para o servidor não é uma brecha para ignorar a escolha da pessoa. Carregue o estado de consentimento junto do pedido e condicione o envio a ele. Da mesma forma, o Measurement Protocol não é lugar para despejar dados pessoais crus em parâmetros custom: envie identificadores técnicos (client_id, user_id, transaction_id) e atributos de negócio, não CPF, e-mail ou telefone em texto plano.
Como depurar#
O modo de falha mais frustrante do Measurement Protocol é o silêncio: você faz o POST, recebe uma resposta de sucesso, e o evento simplesmente não aparece nos relatórios. Isso acontece porque o endpoint de produção aceita quase tudo sem reclamar — payload malformado, parâmetro inválido, client_id estranho, tudo retorna 2xx.
Existem duas ferramentas para não trabalhar às cegas:
O endpoint de validação/debug. Há uma variante de debug do endpoint que retorna uma lista de validationMessages descrevendo problemas no payload antes de você mandar para produção. Use-a em desenvolvimento para pegar nome de evento inválido, parâmetro fora do padrão e tipo errado.
``text # Fluxo recomendado: # 1) Em dev, envie para o endpoint de debug e leia validationMessages. # 2) Confirme que a resposta vem sem erros de validação. # 3) Só então aponte para o endpoint de coleta de produção. ``
O relatório em tempo real e o DebugView. Depois que o payload passa na validação, confirme a chegada no relatório de tempo real do GA4. Marcando o evento com o parâmetro de depuração apropriado, ele aparece no DebugView, onde você vê o evento chegar com todos os seus parâmetros e consegue verificar se value, currency e transaction_id vieram como esperado.
Um roteiro de depuração que resolve a maioria dos casos:
- O
client_idestá correto? Umclient_idinventado desconecta o evento — confira que ele veio do cookie_gareal. currencyacompanhavalue? Sem moeda, a receita some.- O nome do evento e os parâmetros são válidos? Rode pelo endpoint de debug.
- O evento custom foi registrado como dimensão/métrica? Se não, ele é coletado mas não aparece nos relatórios padrão.
- O consentimento foi respeitado? Se você bloqueia por consentimento, confirme que o caso que você está testando deveria mesmo ser enviado.
Onde o Measurement Protocol se encaixa na arquitetura#
Vale situar o Measurement Protocol no quadro maior da mensuração server-side. Ele é o canal do GA4 — do analytics de produto e de jornada. Ele convive com, e não substitui, os canais de conversão das plataformas de anúncio (a Conversions API da Meta, o Enhanced Conversions do Google Ads, os endpoints de conversão de outras redes). Uma arquitetura server-side madura tem, do lado do servidor, um único ponto que recebe o evento de conversão real e o distribui para cada destino no formato que ele espera: o GA4 pelo Measurement Protocol, cada plataforma de anúncio pela sua API. O mesmo pedido confirmado alimenta todos, cada um com o identificador certo — client_id para o GA4, fbc/fbp para a Meta, dados hasheados para o Google.
Feito assim, o evento mais importante do seu negócio — a venda que realmente aconteceu — chega íntegro a todas as ferramentas que dependem dele, independente de o navegador estar aberto, do bloqueador instalado ou da rede ter falhado no último clique. É esse o ganho concreto de mover a mensuração para onde a verdade mora: no seu servidor.