O que é Fingerprinting HTTP/2 Explicado
Fingerprinting HTTP/2 explicado, em essência, é o processo de identificar um cliente de rede pela forma como ele fala o protocolo HTTP/2 — não pelo conteúdo que envia, mas pela sequência e valores dos frames de controle que transmite antes de qualquer payload. Desde 2024, provedores de anti-bot como Akamai, Cloudflare e DataDome passaram a combinar sinais de TLS (JA3/JA4) com sinais de HTTP/2 e HTTP/3 para produzir uma assinatura composta que é praticamente impossível de falsificar sem entender o protocolo em nível de byte.
Para engenheiros de scraping sênior e pesquisadores de segurança, o problema é claro: mesmo que você apresente um certificado TLS impecável e um User-Agent válido, se o seu cliente enviar HEADER_TABLE_SIZE com valor 4096 (o default do httpx e do hyper), qualquer sistema de detecção moderno atribuirá uma pontuação máxima de bot antes mesmo de carregar o HTML. Neste artigo, vamos dissecar cada sinal fingerprintável, explicar por que proxies residenciais continuam sendo obrigatórios, e mostrar uma implementação prática com curl_cffi roteada através de proxies residenciais ProxyHat.
Sinais Fingerprintáveis no Protocolo HTTP/2
O Frame SETTINGS
O RFC 7540 define o frame SETTINGS como o primeiro frame que um cliente HTTP/2 envia após o upgrade TLS. Cada parâmetro nesse frame tem um valor default especificado pelo RFC, mas navegadores reais enviam valores específicos e consistentes. Os parâmetros mais fingerprintáveis são:
- HEADER_TABLE_SIZE (0x01): Tamanho da tabela de compressão HPACK. Chrome 148 envia 65536; Firefox 135 envia 65536; Safari 18 envia 4096. Bibliotecas Python como
httpxeh2enviam 4096 por padrão. - ENABLE_PUSH (0x02): Chrome envia 0 (push desativado). Algumas bibliotecas omitem este parâmetro, o que é imediatamente suspeito.
- INITIAL_WINDOW_SIZE (0x04): Chrome envia 6291456 (6 MB); Firefox envia 131072 (128 KB). O default do RFC é 65535.
- MAX_CONCURRENT_STREAMS (0x03): Chrome envia 1000; Safari envia 500. Bibliotecas muitas vezes omitem este parâmetro.
- MAX_FRAME_SIZE (0x05): Chrome envia 16384; o default RFC é 16384, então este é menos discriminante.
- MAX_HEADER_LIST_SIZE (0x06): Chrome envia 262144 (256 KB). Bibliotecas frequentemente omitem.
A ordem em que esses parâmetros aparecem no frame SETTINGS também é fingerprintável. O Chrome sempre envia na ordem: HEADER_TABLE_SIZE, ENABLE_PUSH, INITIAL_WINDOW_SIZE, MAX_CONCURRENT_STREAMS. Se a sua biblioteca envia em ordem diferente, isso é um sinal.
WINDOW_UPDATE e Prioridade de Streams
Logo após o SETTINGS, o cliente envia um frame WINDOW_UPDATE na conexão (stream 0) com um valor de incremento de janela. Chrome envia 15663105 (15 MB), que combinado com o INITIAL_WINDOW_SIZE de 6 MB resulta em uma janela efetiva de ~22 MB. Bibliotecas Python tipicamente enviam valores default ou omitidos.
A prioridade de streams — especificamente o frame PRIORITY (HTTP/2) ou a prioridade hierárquica (HTTP/2 com dependências) — também é usada. O Chrome 148 usa um esquema de prioridade específico onde o stream 0 tem peso 256 e os streams de recursos têm dependências específicas. Em HTTP/2, o formato PRIORITY foi removido no RFC 9113 (HTTP/2 bis), mas navegadores ainda enviam frames HEADERS com flags de prioridade PRIORITY para compatibilidade.
Ordem de Pseudo-Headers (m, a, s, p)
HTTP/2 usa pseudo-headers especiais que começam com :. A ordem em que aparecem é um sinal forte. Os navegadores enviam nesta ordem:
:method(m):authority(a):scheme(s):path(p)
Bibliotecas como httpx frequentemente enviam :scheme antes de :authority, ou usam host em vez de :authority. Essa inversão é detectada instantaneamente.
O Fingerprint Compacto da Akamai
A Akamai popularizou um formato de string compacta que codifica todos esses sinais em uma única linha. O formato é:
2,h2,a,m,p|a,s,p|WINDOW_UPDATE_VALUE|PRIORITY_FRAME_FORMAT</code>
Por exemplo, o fingerprint HTTP/2 do Chrome 148 em uma conexão típica seria algo como:
1:65536;2:0;4:6291456;3:1000|15663105|m,a,s,p
Onde 1:65536 é HEADER_TABLE_SIZE=65536, 2:0 é ENABLE_PUSH=0, 4:6291456 é INITIAL_WINDOW_SIZE=6291456, 3:1000 é MAX_CONCURRENT_STREAMS=1000. Depois do | vem o valor do WINDOW_UPDATE, e depois do segundo | vem a ordem dos pseudo-headers.
Este fingerprint é então comparado com uma base de dados de fingerprints conhecidos. Se não houver match, o cliente recebe uma pontuação de bot elevada.
JA4H: O Fingerprint HTTP Composto
O JA4H (FoxIO) é uma extensão do conceito JA4 que inclui sinais HTTP. O formato JA4H concatena: método HTTP, versão do protocolo, número de headers, lista de headers ordenada, e um hash dos valores dos headers críticos. Por exemplo:
ge11nn13enus|accca-0000000000000000000000000000000000000000
Onde ge indica GET, 11 indica 11 headers, nn indica que não há cookies, 13 indica Accept-Language com 13 caracteres, enus é o hash do Accept-Language. A segunda parte é um hash dos valores dos headers Accept, Accept-Encoding, Accept-Language, etc.
O JA4H é particularmente poderoso porque combina sinais de HTTP/1.1 e HTTP/2. Se o seu cliente envia um JA4H que não corresponde a nenhum navegador conhecido, o bloqueio é automático.
Por Que HTTP/2 e TLS Precisam Concordar
O ponto crítico que muitos engenheiros de scraping perdem é que o fingerprint TLS (JA3/JA4) e o fingerprint HTTP/2 devem ser coerentes entre si. Se o seu JA4 afirma ser Chrome 148 no Windows, mas o seu frame SETTINGS HTTP/2 tem HEADER_TABLE_SIZE=4096, você tem uma contradição irrefutável.
O Chrome 148 no Windows envia:
- JA4:
t13d1516h2_8daaf6152771_b0da82dd1658(exemplo, varia por build) - SETTINGS:
1:65536;2:0;4:6291456;3:1000 - WINDOW_UPDATE:
15663105 - Pseudo-headers:
m,a,s,p
Se qualquer um desses valores não corresponder ao que o JA4 promete, o sistema anti-bot detecta a inconsistência. A tabela abaixo mostra quais clientes Python/Node populares vazam frames inconsistentes:
| Cliente | HEADER_TABLE_SIZE | INITIAL_WINDOW_SIZE | Pseudo-header Order | Coerente com Chrome? |
|---|---|---|---|---|
httpx 0.28 |
4096 | 65535 | m,s,a,p |
Não |
aiohttp 3.10 |
4096 | 65535 | m,a,s,p |
Parcial |
requests (HTTP/1.1) |
N/A | N/A | N/A | N/A (sem HTTP/2) |
curl_cffi 0.7+ |
65536 | 6291456 | m,a,s,p |
Sim (com impersonate) |
got (Node.js) |
4096 | 65535 | m,a,s,p |
Não |
undici (Node.js) |
4096 | 65535 | m,s,a,p |
Não |
| Playwright (Chromium real) | 65536 | 6291456 | m,a,s,p |
Sim |
Como se vê, apenas curl_cffi com impersonate="chrome e navegadores reais (Playwright, Puppeteer) produzem fingerprints HTTP/2 coerentes com Chrome. Todos os outros clientes Python e Node.js mainstream vazam sinais incompatíveis.
Por Que Proxies Residenciais Ainda São Obrigatórios
Mesmo com um fingerprint HTTP/2 perfeito e TLS impecável, você ainda será bloqueado se o seu IP tiver reputação de datacenter. Sistemas anti-bot modernos como DataDome e PerimeterX atribuem 40-60% da pontuação de bot apenas à reputação do IP, independentemente do fingerprint do protocolo.
Um IP de datacenter da AWS, DigitalOcean ou Hetzner carrega uma penalidade de reputação que nenhum fingerprint HTTP/2 pode compensar. Sistemas anti-bot cruzam o ASN do IP com listas de provedores de cloud conhecidos e atribuem uma pontuação base elevada antes mesmo de inspecionar o TLS ou HTTP/2.
Proxies residenciais resolvem isso porque o IP de saída pertence a um ISP residencial legítimo, com ASN de provedor de banda larga (Comcast, AT&T, Vodafone, etc.). A reputação do IP é neutra ou positiva, permitindo que o fingerprint do protocolo seja avaliado sem viés prévio.
É por isso que uma estratégia de scraping robusta em 2026 requer ambos: fingerprint HTTP/2 coerente e proxy residencial. Fazer apenas um dos dois não é suficiente.
Implementação Prática com curl_cffi e ProxyHat
A abordagem mais limpa para apresentar um fingerprint HTTP/2 coerente do Chrome é usar curl_cffi, que é um binding Python para curl-impersonate — uma versão modificada do curl que reproduz exatamente os frames TLS e HTTP/2 de navegadores reais.
Instalação
pip install curl_cffi
Exemplo: GET Simples com Proxy Residencial ProxyHat
from curl_cffi import requests
# Proxy residencial ProxyHat com geo-targeting EUA
proxy_url = "http://user-country-US:pass@gate.proxyhat.com:8080"
session = requests.Session(impersonate="chrome")
response = session.get(
"https://httpbin.org/headers",
proxies={"http": proxy_url, "https": proxy_url},
timeout=30
)
print(f"Status: {response.status_code}")
print(f"JA4H coerente: {response.headers.get('x-ja4h', 'N/A')}")
print(response.json())
Neste exemplo, impersonate="chrome" faz com que curl_cffi reproduza o fingerprint TLS e HTTP/2 do Chrome mais recente. O proxy ProxyHat fornece o IP residencial. A combinação produz uma assinatura que é indistinguível de um Chrome real em uma conexão residencial.
Exemplo: Sessão Sticky com Rotação Controlada
from curl_cffi import requests
import uuid
# Sessão sticky — mesmo IP por 10 minutos
session_id = str(uuid.uuid4())[:8]
proxy_url = f"http://user-session-{session_id}:pass@gate.proxyhat.com:8080"
session = requests.Session(impersonate="chrome")
# Primeira requisição — estabelece sessão
r1 = session.get(
"https://exemplo.com",
proxies={"http": proxy_url, "https": proxy_url},
timeout=30
)
print(f"Req 1: {r1.status_code}")
# Segunda requisição — mesmo IP (sessão sticky)
r2 = session.get(
"https://exemplo.com/pagina2",
proxies={"http": proxy_url, "https": proxy_url},
timeout=30
)
print(f"Req 2: {r2.status_code}")
Exemplo: Rotação Por Requisição
from curl_cffi import requests
import uuid
urls = [
"https://exemplo.com/p1",
"https://exemplo.com/p2",
"https://exemplo.com/p3",
]
session = requests.Session(impersonate="chrome")
for url in urls:
# Nova sessão a cada requisição = novo IP
sid = str(uuid.uuid4())[:8]
proxy = f"http://user-session-{sid}:pass@gate.proxyhat.com:8080"
r = session.get(
url,
proxies={"http": proxy, "https": proxy},
timeout=30
)
print(f"{url} -> {r.status_code} ({len(r.content)} bytes)")
Verificando Seu Fingerprint HTTP/2
Para confirmar que seu fingerprint está coerente, você pode usar o endpoint de debug do tls.peet.ws que retorna o fingerprint JA4 e HTTP/2 completo:
from curl_cffi import requests
import json
proxy = "http://user-country-US:pass@gate.proxyhat.com:8080"
session = requests.Session(impersonate="chrome")
r = session.get(
"https://tls.peet.ws/api/all",
proxies={"http": proxy, "https": proxy},
timeout=30
)
data = r.json()
print(f"JA4: {data.get('tls', {}).get('ja4', 'N/A')}")
print(f"JA4H: {data.get('http', {}).get('ja4h', 'N/A')}")
print(f"Akamai h2: {data.get('http2', {}).get('akamai_fingerprint', 'N/A')}")
Se tudo estiver correto, você verá um JA4 que corresponde ao Chrome, um JA4H coerente, e um fingerprint Akamai HTTP/2 no formato 1:65536;2:0;4:6291456;3:1000|15663105|m,a,s,p.
Erros Comuns e Casos de Borda
1. Usar httpx com impersonate TLS mas HTTP/2 nativo
Alguns engenheiros configuram httpx com um cliente TLS customizado que imita o JA4 do Chrome, mas esquecem que o httpx ainda envia HEADER_TABLE_SIZE=4096. O resultado: JA4 diz Chrome, HTTP/2 diz biblioteca Python. Bloqueio instantâneo.
2. Omitir ENABLE_PUSH
O Chrome sempre envia ENABLE_PUSH=0. Se o seu cliente omite este parâmetro, o servidor anti-bot percebe a ausência e atribui pontuação de bot. curl_cffi com impersonate envia corretamente.
3. Ordem de Headers HTTP Inconsistente
Mesmo com HTTP/2 correto, a ordem dos headers HTTP (não pseudo-headers) importa para o JA4H. O Chrome envia headers nesta ordem típica: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform, upgrade-insecure-requests, user-agent, accept, sec-fetch-site, sec-fetch-mode, sec-fetch-user, sec-fetch-dest, accept-encoding, accept-language, cookie. Se a sua biblioteca envia em ordem alfabética ou arbitrária, o JA4H não corresponderá.
4. HTTP/3 (QUIC) e Fingerprinting de QLOG
Com a adoção crescente de HTTP/3, o fingerprinting se estende ao QUIC. O RFC 9114 define HTTP/3 sobre QUIC, e os parâmetros de transporte QUIC (initial max data, max streams, idle timeout) são igualmente fingerprintáveis. O Chrome 148 envia parâmetros QUIC específicos que bibliotecas como aioquic não reproduzem por padrão. Se o servidor suporta HTTP/3 e o cliente faz upgrade, o fingerprint QUIC entra na equação.
5. Não Respeitar robots.txt e Termos de Serviço
Do ponto de vista ético e legal, fingerprinting HTTP/2 para acessar conteúdo que o site explicitamente proíbe via robots.txt ou Termos de Serviço pode constituir violação do CFAA (Computer Fraud and Abuse Act) nos EUA ou violação do GDPR na Europa se dados pessoais forem coletados sem base legal. Use estas técnicas apenas para pesquisa de segurança autorizada, monitoramento legítimo de seus próprios recursos, ou acesso a APIs públicas.
Configuração ProxyHat para Fingerprinting Coerente
Para maximizar a coerência do fingerprint, recomendo a seguinte configuração ProxyHat:
- Tipo de proxy: Residencial (não datacenter) — veja nossos planos.
- Geo-targeting: Use
user-country-USse o seu fingerprint JA4 indica Chrome no Windows em inglês. Inconsistência geo-IP vs. Accept-Language é outro sinal. Consulte nossas localizações. - Sessão: Use sessões sticky para fluxos multi-página (login, navegação). Rotação por requisição para scraping em larga escala. Veja nosso guia de web scraping.
- Porta: 8080 para HTTP, 1080 para SOCKS5. Sempre
gate.proxyhat.com.
Exemplo SOCKS5 com curl_cffi
from curl_cffi import requests
proxy = "socks5://user-country-DE-city-berlin:pass@gate.proxyhat.com:1080"
session = requests.Session(impersonate="chrome")
r = session.get(
"https://exemplo.de",
proxies={"http": proxy, "https": proxy},
timeout=30
)
print(f"Status: {r.status_code}")
Para mais detalhes técnicos, consulte a documentação oficial do ProxyHat.
Casos de Uso Apropriados
As técnicas descritas neste artigo são apropriadas para:
- Pesquisa de segurança autorizada: Testes de penetração em sistemas próprios ou com autorização explícita.
- Monitoramento de SERP legítimo: Verificação de posicionamento de suas próprias páginas. Veja nosso guia de SERP tracking.
- Coleta de dados públicos: Acesso a APIs públicas ou páginas sem restrições explícitas em
robots.txt. - QA e testes automatizados: Verificação de comportamento de seus próprios serviços.
Não é apropriado para: fraude de tickets, evasão de bans, scraping de dados pessoais sem consentimento (violação do GDPR), acesso não autorizado a sistemas protegidos (violação do CFAA), ou qualquer atividade que viole os Termos de Serviço do site alvo.
Aviso legal: O CFAA (18 U.S.C. § 1030) criminaliza acesso não autorizado a sistemas protegidos. Na UE, o GDPR (Regulamento 2016/679) regula o processamento de dados pessoais. Verifique a legalidade da sua atividade com um advogado antes de implementar estas técnicas em produção.
Principais Conclusões
- Fingerprinting HTTP/2 explicado: Sistemas anti-bot modernos combinam JA3/JA4 (TLS) com fingerprints HTTP/2 (SETTINGS, WINDOW_UPDATE, pseudo-header order) e JA4H (HTTP) para identificar bots com precisão >95%.
- Coerência é tudo: Um JA4 que afirma Chrome mas envia
HEADER_TABLE_SIZE=4096é uma contradição fatal. Usecurl_cfficomimpersonateou navegadores reais. - Proxies residenciais são obrigatórios: Fingerprint HTTP/2 perfeito + IP de datacenter = bloqueio. A reputação do IP contribui com 40-60% da pontuação de bot.
- ProxyHat + curl_cffi: A combinação de proxies residenciais ProxyHat com
curl_cffi impersonate="chrome"produz uma assinatura indistinguível de Chrome real em conexão residencial. - Ética e legalidade: Use apenas para pesquisa autorizada e acesso a dados públicos. Violações de CFAA e GDPR têm consequências sérias.






