2026.06.11 (Qui)

✨ Resumo do GPT-5.5  

Registro de como reduzi tráfego de crawlers cloud e acessos headless leves na contagem do Cloudflare Worker, adicionando filtros de ASN/organização, sinais de visible engagement e liberando um ASN de ISP bloqueado por engano.

Os números de analytics ficaram estranhos de novo.

Depois de adicionar a página pública de estatísticas, o trabalho local já não poluía o contador de produção. Mas apareceu outro problema.

Algumas requisições não pareciam leitura humana.

Elas abriam várias rotas rapidamente, usavam User-Agent parecido com navegador, mas não se comportavam como um leitor ficando na página. Crawlers são esperados num blog estático público. O problema é que, se eles também chamam /track, as visualizações e visitas públicas crescem junto.

Quando adicionei o contador, escrevi que bots e visitas duplicadas estavam razoavelmente bloqueados. Na operação real, esse “razoavelmente” precisava subir um pouco.

User-Agent não bastava

O Worker já tinha verificações básicas.

bot user-agent
Cloudflare verified bot
Cloudflare bot score
dedupe window

Isso pega bots óbvios.

Mas nem todo crawler escreve bot no User-Agent. Algumas requisições parecem Chrome normal. Só com User-Agent fica difícil separar uma pessoa de um navegador automatizado fino.

Os dados request.cf da Cloudflare incluem ASN e organização ASN. Fiz /track verificar isso também.

TRACK_BLOCKED_ASNS
TRACK_BLOCKED_AS_ORGS

As variáveis de ambiente não substituem totalmente os defaults. Elas são somadas aos defaults do Worker. Assim, eixos claramente bloqueados ficam no código, e descobertas operacionais podem entrar pelo ambiente.

A comparação exata de organização era fraca.

Huawei Cloud
Huawei-Cloud-HK
Huawei Cloud Singapore POP
Huawei Clouds Singapore

Os nomes variam. Por isso, a comparação aceita correspondência exata e texto contido. Um default consegue cobrir várias variantes.

Adicionar 3 segundos de visible engagement

Filtro de ASN sozinho é centrado demais na rede.

Um leitor real pode estar atrás de um ISP ou rede corporativa. Um crawler também pode vir de uma rede comum. Então era preciso olhar sinais do cliente.

Agora o cliente só envia /track depois de a página ficar visível por um curto tempo.

track_delay_seconds = 3

Três segundos não é muito. Um leitor real quase não percebe. Mas isso filtra previews instantâneos, scraping em abas ocultas e carregamentos que somem imediatamente.

O cliente manda estes valores no payload.

engagementMs
visibilityState
documentHidden
viewportWidth
viewportHeight

O Worker valida tudo de novo.

Não basta confiar que o JS esperou. Se engagementMs for menor que o limite, a requisição é ignorada como client_visible_too_short. Se o viewport for zero ou inválido, é ignorada como client_viewport_invalid.

Sinais visíveis não eram opcionais

A primeira implementação ainda era frouxa.

Ela verificava visibilityState e documentHidden quando existiam, mas uma requisição sem esses campos ainda podia passar. Isso endurecia o cliente novo, mas deixava vivas requisições manuais a /track sem campos.

Então a condição do Worker ficou estrita.

visibilityState === "visible"
documentHidden === false

Se as duas não batem exatamente, a requisição é ignorada como client_signal_missing.

A ideia é tratar hidden e missing como a mesma família. /track é o endpoint que aumenta contadores públicos. Uma requisição ali precisa parecer enviada pela página normal e pelo cliente normal. Sem sinais, não deve passar.

/analytics continua público, mas /track ficou mais exigente.

Ler estatísticas
-> público

Aumentar visualizações
-> production origin
-> visible engagement
-> viewport válido
-> passa filtros bot/ASN/org

Valores bloqueados precisam ser revisados

Esse trabalho também mostrou que uma blocklist não melhora só por crescer.

Ao tentar pegar crawlers cloud, dá para bloquear por engano um ASN de ISP real. Isso derruba registros de leitores reais. Por isso removi do bloqueio embutido o ASN adicionado por erro.

Filtros fortes parecem melhores, mas false positives também têm custo.

Contar bots infla o número.
Bloquear pessoas apaga leitores reais.

Os dois são ruins.

O critério ficou assim.

Bloquear eixos claramente cloud/crawler no Worker.
Exigir sinais visíveis do cliente.
Não colocar tráfego tipo ISP humano nos bloqueios embutidos.
Guardar valores suspeitos com ignored_reason para auditoria.

analytics_events guarda linhas mínimas com ignored_reason até para requisições ignoradas. Sem isso, depois fica difícil explicar por que um número caiu ou o que foi bloqueado.

Os números devem aparecer rápido, mas não fácil demais

Um contador de visitas vive num equilíbrio estranho.

Se atualiza devagar demais, parece quebrado. Por isso o trabalho do contador usava baseline do GA com incrementos imediatos no D1.

Mas se contar é fácil demais, bots também contam.

Esta mudança leva o equilíbrio um pouco para o lado da cautela. Leitores normais ainda contam depois de 3 segundos. Mas automação instantânea, documentos ocultos, viewports inválidos e eixos cloud conhecidos entram com mais dificuldade.

Estatística pública não é livro contábil.

Ainda assim, deve se aproximar de um sinal de que alguém leu. Números grandes não são o objetivo. Números confiáveis permitem decidir melhor.

O que verifiquei

Verifiquei isto durante o trabalho.

node --check cloudflare/ga-stats-worker.js
node --check assets/js/custom/visitor-stats.js
git diff --check
bundle exec jekyll build
Cloudflare Worker deploy
GitHub Pages deploy

Também fiz smoke tests do Worker.

/analytics?range=today
-> trackDelaySeconds: 3

/track without visibilityState
-> ignored, client_signal_missing

/track with visibilityState="hidden"
-> ignored, client_signal_missing

/track with documentHidden=true
-> ignored, client_signal_missing

Também confirmei que o HTML publicado da home continha data-track-delay-seconds="3".

Não foi uma mudança chamativa.

Mas números públicos precisam desse tipo de defesa. Visualizações são visíveis, então contagem errada quebra confiança rápido. Este trabalho não deixou o contador maior; deixou o contador mais difícil de enganar.

Deixe um comentário