Extração de dados

Configurando os cabeçalhos e o User-Agent corretos com proxies residenciais

Um IP residencial limpo te leva até a porta. Os cabeçalhos decidem se você parece um navegador depois de passar por ela, e a consistência importa mais do que qualquer valor isolado.

Chris Collins

Chris Collins

26 de agosto de 2026 · 9 min de leitura

Um padrão comum em conversas de suporte: alguém migra para proxies residenciais, a taxa de bloqueio melhora e, ainda assim, um subconjunto obstinado de destinos continua recusando as requisições. O IP está limpo, a geografia está correta, e as requisições continuam sendo desafiadas. Quase sempre a resposta está nos headers, porque o endereço leva você até a porta, mas os headers decidem se o que entra parece um navegador.

O erro por trás da maioria dos problemas com headers é pensar neles como uma lista de valores a serem configurados corretamente. Não é isso. É um conjunto de afirmações que precisam concordar entre si e com tudo mais na sua conexão, e uma requisição construída com partes incompatíveis é mais suspeita do que uma sem nenhum esforço.

Consistência supera qualquer valor individual

Comece por aqui, porque isso reformula tudo o que vem depois. Um servidor não avalia o seu User-Agent isoladamente. Ele vê um conjunto: a string do UA, os client hints que a acompanham, o conjunto de headers e sua ordem, o valor de Accept-Language, o handshake TLS por trás de tudo, e o IP de onde tudo isso chegou. Navegadores reais produzem conjuntos internamente consistentes porque o mesmo software gerou tudo.

Scrapers produzem conjuntos inconsistentes por acidente. Um UA de Chrome chegando com o conjunto de headers de uma biblioteca HTTP em Python, um UA de Windows sobre uma fingerprint TLS que pertence a uma ferramenta Linux, uma saída nos EUA enviando Accept-Language: de-DE, ou um navegador declarado que nunca solicita os recursos que um navegador solicitaria. Nenhum desses sinais isoladamente diz “bot”, mas a contradição diz, e contradições são muito mais fáceis de detectar de forma confiável do que qualquer sinal isolado. Essa é a mesma lógica do pareamento entre dispositivo e rede nos antidetect browsers: cada camada precisa contar a mesma história.

Portanto, o objetivo não é a string de UA mais convincente. É uma requisição em que o UA, os headers, a fingerprint TLS e o endereço de saída descrevem todos o mesmo visitante plausível.

O que um navegador real realmente envia

Se você definir apenas o User-Agent, você já está sendo inconsistente, porque nenhum navegador envia um UA e nada mais. Uma requisição de página de um Chrome moderno carrega, no mínimo, um conjunto como este:

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br, zstd
sec-ch-ua: "Chromium";v="141", "Not?A_Brand";v="24", "Google Chrome";v="141"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1

Três coisas ali vale a pena entender, e não apenas copiar.

Os client hints precisam corresponder ao UA. A lista de marcas do sec-ch-ua, o sec-ch-ua-platform e o sec-ch-ua-mobile são uma reafirmação estruturada do que a string do UA já declara. Se o seu UA diz Chrome no Windows e o seu hint de plataforma diz macOS, ou se a versão na lista de marcas não coincide com a versão no UA, você se contradisse em dois headers adjacentes. Não enviar nenhum client hint enquanto afirma ser um Chrome recente também é uma incompatibilidade, já que um Chrome real os envia.

Os headers Sec-Fetch descrevem o contexto. Eles informam ao servidor que tipo de requisição é esta: uma navegação de nível superior, a busca de um subrecurso, um XHR de mesma origem. O carregamento de uma página é Dest: document, Mode: navigate, Site: none quando aberta diretamente, ou same-origin quando segue um link interno. Um XHR para uma API é Dest: empty, Mode: cors. Errar isso é revelador justamente porque esses valores são automáticos em um navegador e fáceis de esquecer em um script.

Accept-Encoding é uma afirmação que você precisa honrar. Anuncie br e zstd apenas se o seu cliente realmente conseguir descomprimi-los. Algumas bibliotecas anunciam codificações que depois não conseguem tratar, o que produz erros ou um fallback diferente do que um navegador negociaria.

Combine o Accept-Language com o país de saída

Este caso é específico do trabalho com proxies e é a incompatibilidade autoinfligida mais comum. Se você sai por um IP residencial alemão e envia Accept-Language: en-US, você descreveu um visitante cujo navegador está configurado para inglês americano, mas está em uma conexão residencial alemã. Isso acontece na vida real, mas é raro o suficiente para ser um sinal, e, na prática, isso pode mudar o que você recebe de volta: muitos sites servem conteúdo com base nesse header, então um job de coleta com segmentação geográfica pode obter o idioma errado enquanto parece estar funcionando.

Vincule o idioma à saída, idealmente no mesmo lugar em que você escolhe o país, para que os dois nunca se desalinhem:

import requests

MARKETS = {
    "us": "en-US,en;q=0.9",
    "de": "de-DE,de;q=0.9,en;q=0.8",
    "fr": "fr-FR,fr;q=0.9,en;q=0.8",
    "br": "pt-BR,pt;q=0.9,en;q=0.8",
}

UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
      "(KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36")

def fetch(url, country):
    proxy = f"http://customer-USERNAME-country-{country}:PASSWORD@p.shifter.io:443"
    headers = {
        "User-Agent": UA,
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,"
                  "image/avif,image/webp,*/*;q=0.8",
        "Accept-Language": MARKETS[country],      # follows the exit, always
        "Accept-Encoding": "gzip, deflate, br",
        "sec-ch-ua": '"Chromium";v="141", "Not?A_Brand";v="24", '
                     '"Google Chrome";v="141"',
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": '"Windows"',        # agrees with the UA
        "Sec-Fetch-Dest": "document",
        "Sec-Fetch-Mode": "navigate",
        "Sec-Fetch-Site": "none",
        "Upgrade-Insecure-Requests": "1",
    }
    return requests.get(url, headers=headers,
                        proxies={"http": proxy, "https": proxy}, timeout=20)

A mesma disciplina se aplica a timezone e locale quando você está controlando um navegador, casos em que esses valores são observáveis separadamente e também devem corresponder à saída, o que se torna mais rigoroso quando você trabalha com segmentação em nível de cidade.

Não gire o User-Agent de forma aleatória

Este é um conselho que circula amplamente e causa mais mal do que bem. Escolher um UA aleatório por requisição produz um padrão que nenhuma população real apresenta: um IP que é Chrome no Windows, depois Safari em um Mac, depois Firefox no Linux, em menos de um minuto. Pior, se você mantém uma sticky session para que um único endereço atenda a um fluxo de várias etapas, trocar o UA no meio do fluxo significa que o mesmo visitante aparentemente trocou de dispositivo entre clicar em uma busca e visualizar um resultado.

O modelo coerente é uma identidade por sessão. Escolha um UA plausível, mantenha-o durante toda a vida daquela sessão, e mantenha tudo o mais consistente com ele. Se você quiser variedade em toda a sua frota, varie entre sessões, e não entre requisições, e varie em proporções realistas, e não de forma uniforme entre todos os navegadores que já existiram. Mantenha as versões atualizadas também: um UA que declara uma versão de navegador de três anos atrás já é, por si só, anômalo, já que instalações reais são atualizadas.

Headers são apenas uma camada

Vale a pena ser honesto sobre o limite aqui. Headers perfeitos não fazem um cliente Python parecer o Chrome, porque a camada abaixo ainda é diferente. O handshake TLS e as configurações de HTTP/2 que o seu cliente produz formam uma fingerprint própria, e um UA de Chrome sobre uma fingerprint que indica Python é exatamente a contradição discutida no início. Essa incompatibilidade é o tema de fingerprinting de TLS e HTTP/2, e é por isso que alguns destinos fortemente protegidos permanecem fora de alcance para clientes HTTP simples, independentemente do trabalho feito com os headers, o que é um dos motivos para recorrer a um navegador headless.

A ordem dos headers importa pelo mesmo motivo. Navegadores emitem headers em uma ordem estável; muitas bibliotecas HTTP os emitem em ordem alfabética ou na ordem de inserção, o que é outra forma de o conjunto revelar sua origem mesmo quando todos os valores estão corretos. Alguns clientes permitem controlar a ordenação, e, quando isso importa, reproduzir a ordem de um navegador real vale o esforço. O catálogo mais amplo desses sinais está em as fingerprints que podem bloquear a extração de dados, e as versões comuns autoinfligidas estão em erros que disparam detecção.

Uma checklist rápida

Envie um conjunto completo de headers de navegador, não apenas um User-Agent. Faça os client hints concordarem com o UA em marca, versão, plataforma e flag de dispositivo móvel. Defina os headers Sec-Fetch para descrever o tipo real de requisição. Vincule o Accept-Language ao país de saída e mantenha-os no mesmo trecho de código. Anuncie apenas as codificações que você consegue decodificar. Mantenha uma identidade por sessão, em vez de girar por requisição, e mantenha as versões do UA atualizadas. Depois, verifique se a sua fingerprint TLS concorda com o navegador que você afirma ser, porque essa é a camada que os headers não conseguem corrigir.

Se um destino ainda recusar você depois de tudo isso, o problema se deslocou para outro lugar: ritmo, reputação de IP, ou sinais comportamentais, que são o conjunto mais amplo abordado em evitando bloqueios e fazendo scraping de sites fortemente protegidos.

Conclusão

Os headers são a segunda metade da identidade que o seu proxy inicia. Um endereço residencial limpo torna a conexão discreta; um conjunto coerente de headers torna a requisição discreta, e coerência é o jogo todo. Cada afirmação precisa concordar com todas as outras: os client hints com o UA, o idioma com o país de saída, os metadados de fetch com o tipo de requisição, as codificações com suas capacidades reais, e o conjunto todo com a fingerprint TLS por trás dele. Uma identidade por sessão, mantida estável, supera qualquer rotação inteligente, sempre.

Essa camada de conexão é o que os proxies residenciais oferecem, endereços reais de qualidade residencial com segmentação por país e cidade, para que a geografia que os seus headers afirmam seja a geografia pela qual você realmente está saindo, cobrados por GB, de modo que ajustar as requisições para buscar menos custa menos.

Pronto para começar?

Experimente os proxies residenciais da Shifter, mais de 205M IPs, mais de 195 países, a partir de $0,75/GB.

Começar