Pular para o conteúdo
Sunstone Apps
← Voltar para o blog
Operação de estúdio

O que muda quando um site institucional começa a usar analytics

Um site institucional parece inofensivo até começar a carregar analytics. A partir daí ele deixa de ser só uma vitrine estática. Ele ganha uma superfície própria de tratamento de dados, copy de privacidade e obrigações de navegação. O trabalho é pequeno, mas precisa ser explícito.

Publicado: 8 min read
Imagem de prévia OpenGraph deste artigo. O que muda quando um site institucional começa a usar analytics

Um site institucional pode parecer a parte menos arriscada de um estúdio de apps. Ele tem uma home, um blog, alguns links para apps e um email de contato. Não tem conta. Não tem checkout. Não tem conteúdo enviado por usuários. Não tem estado de jogo. Comparado com um app mobile que carrega anúncios, compras, crash reporting e analytics, o site do estúdio parece quase inerte.

Essa premissa quebrou no momento em que adicionamos IDs reais de analytics ao hub da Sunstone Apps.

Os IDs em si não eram segredo. Um measurement ID do GA4 e um token do Cloudflare Web Analytics foram feitos para aparecer no HTML client-side. O problema não era vazamento de credencial. O problema era o que o site virava quando esses scripts passavam a rodar: uma propriedade web que mede visitas, páginas, origem de tráfego, dispositivos, região aproximada e contexto técnico de requisição.

Isso não quer dizer que o site precisava virar um portal jurídico pesado. Quer dizer que o visitante merecia uma política de privacidade clara, e que o código precisava de um lugar único para linkar essa política em todas as páginas.

A correção acabou tocando três áreas que é fácil tratar separadamente, mas que não devem ficar separadas por muito tempo: configuração de analytics, comportamento legal/SEO e chrome do site.

Uma vitrine estática não é o mesmo que um site medido

O primeiro impulso foi tratar o site institucional como uma vitrine.

Isso funciona enquanto o site só publica páginas e links. No momento em que ele carrega analytics, a pergunta muda. O site passa a ter uma superfície própria de disclosure, mesmo sem contas, sem formulários além do contato por email e sem estado de produto.

As políticas dos apps podem explicar comportamento dos apps. Elas não devem ser esticadas para cobrir todos os scripts de sunstoneapps.com. Quem lê um post no site do estúdio não deveria precisar inferir que analytics do site estão descritos dentro da política de um app mobile.

A regra prática que adotamos ficou simples:

  • se o site institucional não tem analytics client-side, uma página pública de privacidade pode ser menos urgente;
  • se o site institucional carrega GA4 ou Cloudflare Web Analytics, ele deve ter uma política curta para esse site;
  • essa política não deve fingir que o site coleta dados do app, compras, localização precisa, contatos, câmera, microfone ou qualquer coisa que ele não toca.

A página criada é estreita de propósito. Ela diz que o site usa Cloudflare Web Analytics e Google Analytics para medição de tráfego, menciona dados técnicos de entrega processados por hospedagem/CDN/segurança e aponta solicitações de privacidade para [email protected]. Ela não fala de anúncios mobile, compras, saves de app ou progresso de jogo. Isso pertence às políticas específicas dos apps.

Essa fronteira importa. Copy de privacidade ampla demais não é mais segura. Ela faz um site pequeno parecer que coleta mais do que coleta.

A decisão seguinte foi SEO.

Já tínhamos aprendido isso nos sites dos apps: boilerplate legal não precisa disputar tráfego orgânico. Páginas de privacidade, termos e exclusão de dados precisam estar acessíveis, mas não precisam aparecer no sitemap nem virar landing page de busca.

Então a política do site institucional segue o mesmo padrão:

  • renderiza pelo layout normal do Astro;
  • tem canonical e hreflang;
  • é linkada no footer;
  • usa noIndex={true};
  • é filtrada do sitemap.

A armadilha era adicionar um Disallow no robots.txt para /privacy-policy/. A frase parece certa quando o objetivo é “não indexar isso”. Mas não é o melhor mecanismo.

Se o crawler é bloqueado por robots.txt, ele pode não acessar a página e não ver o meta noindex. Para uma página nova, nunca indexada, o risco prático é pequeno. Para uma URL já conhecida pelo Google, bloquear o crawl pode tornar a limpeza mais lenta ou confusa. O crawler sabe que a URL existe, mas não consegue ler a página que pede remoção do índice.

A regra mais limpa é a que usamos agora nos sites: manter robots.txt aberto, publicar sitemap, filtrar páginas legais do sitemap e deixar a própria página legal declarar noindex.

O robots.txt fica sem drama:

User-agent: *
Allow: /

Sitemap: https://sunstoneapps.com/sitemap-index.xml

Esse tédio é intencional. robots.txt não deve virar painel de controle de SEO.

Também validamos a saída de produção, não só o código fonte. Uma página pode parecer correta no componente Astro enquanto o HTML gerado conta outra história. Checamos que /privacy-policy/ renderiza noindex,nofollow, que privacy-policy não aparece no sitemap gerado e que o robots.txt final continua aberto. É o mesmo hábito descrito no nosso checklist de indexação para sites Astro no Cloudflare Pages: o Google indexa output, não intenção.

A página expôs um problema de chrome

A política não estava visualmente errada porque páginas legais são especiais. Ela estava errada porque o chrome do site tinha drift.

A home tinha um footer. O índice do blog tinha outro. Posts do blog tinham arrays de footer próprios. A página legal nova copiava um padrão dos sites de app. O 404 tinha mais uma lista local de header e footer, além de um script pequeno para trocar textos em português.

Cada cópia fazia sentido quando foi escrita. Juntas, elas viraram bug de manutenção.

Adicionar o link de privacidade na home não adicionava o link no blog. Adicionar links sociais na home não colocava esses links na página legal. Ajustar links de idioma em um lugar não corrigia posts. As páginas pareciam pertencer ao mesmo site, mas o contrato real de navegação estava espalhado por componentes e arquivos de conteúdo.

A política deixou isso evidente porque links legais não são decoração. Se alguém entra por um post do blog, essa pessoa ainda precisa encontrar a política de privacidade. Se um crawler chega direto em um post, os links do footer ainda precisam expor as rotas internas importantes. Sitemap ajuda descoberta, mas não deve ser o único caminho pelo site.

A correção foi criar chrome local compartilhado:

  • SiteHeader.astro encapsula o Header e o ThemeToggle de @apps/site-ui;
  • SiteFooter.astro encapsula o Footer compartilhado;
  • src/lib/chrome.ts concentra navegação por idioma, colunas do footer, aviso legal, label do tema, skip label e comportamento dos links de idioma.

Agora home, índice do blog, posts, política de privacidade e 404 consomem a mesma fonte.

Isso não é mover o design system inteiro para o site. A UI reutilizável continua em @apps/site-ui. O chrome específico do estúdio fica no site porque seus labels e links são decisões de conteúdo de sunstoneapps.com. O ponto importante é ter uma fonte local única, não cinco cópias por template.

Markdown era suficiente

Também corrigimos o formato do conteúdo.

A primeira versão da política usava .mdx porque os sites dos apps já tinham páginas legais em MDX. Mas esta política não renderizava componentes no corpo. Era só frontmatter e Markdown. MDX funcionaria, mas era poder demais para uma página simples.

A regra agora é mais estreita: use .md quando o conteúdo legal não precisar de JSX; use .mdx só quando o conteúdo realmente renderizar componentes. Isso mantém o loader flexível sem fazer toda página legal parecer mais complexa do que é.

É uma regra pequena, mas evita drift futuro. Extensão de arquivo também é superfície de manutenção. Se uma política futura precisar de componente customizado, trocar um arquivo para MDX é fácil. Começar toda página simples como MDX ensina o hábito oposto.

O que vamos checar na próxima vez

Na próxima vez que um site institucional ganhar analytics, não vamos começar pelos IDs de tracking. Vamos começar pela superfície.

O checklist agora é:

  • Os scripts de analytics estão habilitados? Em quais ambientes?
  • Cloudflare Web Analytics, Clarity e Google Analytics renderizam só em produção, nunca em localhost?
  • O site tem uma política que bate com os fluxos reais de dados?
  • O contato de privacidade é o email certo, não o alias geral?
  • A página está linkada de toda entrada pública?
  • Ela está com noindex e fora do sitemap?
  • O robots.txt continua aberto para o crawler ler o noindex?
  • Todos os tipos de página usam a mesma fonte de header e footer?
  • O build de produção prova o HTML, sitemap e robots esperados?
  • O console local continua limpo depois de um reload completo?

Esse último ponto veio do navegador, não do build. O Google Analytics já estava gateado para produção, mas o Cloudflare Web Analytics ainda carregava em localhost. A requisição para https://cloudflareinsights.com/cdn-cgi/rum falhava por CORS no dev porque a Cloudflare respondia para http://localhost, não para http://localhost:4173. A correção foi fazer o AnalyticsScripts.astro compartilhado tratar todos os providers externos do mesmo jeito: sem beacon da Cloudflare, sem Clarity e sem script do GA quando import.meta.env.PROD não for true.

Isso deixa a validação local honesta. Console limpo deve significar que a página está saudável, não que todo mundo aprendeu a ignorar um erro conhecido de analytics. Produção continua medindo; desenvolvimento continua com feedback rápido.

A checagem em produção pegou mais um detalhe. No Dashboard da Cloudflare, a URL de edição do Web Analytics contém um ID, mas o snippet manual pode mostrar outro token dentro de data-cf-beacon. A página pode estar configurada para sunstoneapps.com e ainda assim falhar a coleta se o código usar o ID da tela de edição em vez do token do snippet. O sintoma no navegador era preciso: beacon.min.js carregava, mas /cdn-cgi/rum falhava por CORS e o Dashboard continuava em zero visitas. Depois de trocar para o token copiado em Install JS Snippet, o mesmo endpoint respondeu 204 e o console ficou limpo.

Então a checagem de instalação manual agora é concreta: copie o token do textarea do snippet, inspecione o HTML renderizado e valide o resultado de rede. Ver a linha do site no Dashboard não basta.

Nada disso é glamouroso. Por isso mesmo vale escrever.

Sites pequenos acumulam risco por exceções pequenas: um array de footer aqui, uma página legal ali, um token de analytics adicionado porque é “só um ID público”. A correção não é um framework enorme de compliance. É uma política curta, comportamento de SEO explícito e uma fonte compartilhada de chrome para todas as páginas contarem a mesma história.

Leia o checklist de indexação