Os frameworks de automação de navegador aceitam um proxy como opção de inicialização, mas proxies autenticados exigem tratamento especial porque o Chromium não aceita user:pass@ em uma URL de proxy: o Playwright aceita credenciais como campos separados (e pode definir um proxy diferente por contexto do navegador), o Puppeteer precisa de page.authenticate() e o Selenium precisa de uma extensão ou de um encaminhador local. Este guia fornece código funcional para cada um e, em seguida, aborda a parte que importa mais do que a sintaxe: um navegador headless atrás de um IP residencial ainda é identificável, a menos que o restante de sua história corresponda.
O fato que molda tudo: o Chromium ignora credenciais de proxy na URL
Ferramentas de linha de comando e bibliotecas Python aceitam facilmente http://user:pass@host:port. A flag --proxy-server do Chromium aceita apenas host:port (com um esquema opcional); as credenciais nele são silenciosamente descartadas. Quando o proxy responde com 407, o navegador mostra um diálogo de autenticação — que nenhum script de automação está observando. Portanto, cada framework tem sua própria maneira de fornecer o nome de usuário e a senha, e é aí que a maioria dos problemas de “proxy funciona no curl, mas não no meu script de navegador” vem. O Firefox é mais tolerante em alguns caminhos, mas os mecanismos específicos do framework abaixo funcionam em todos os mecanismos.
Playwright
O Playwright tem suporte de proxy de primeira classe com campos de credenciais separados e — o recurso que o torna a melhor opção para trabalhos com múltiplas identidades — o proxy pode ser definido por contexto do navegador, então um único processo de navegador pode hospedar várias sessões isoladas, cada uma saindo de um IP diferente.
Python
from playwright.sync_api import sync_playwright
PROXY = {"server": "http://proxy.example.com:8080",
"username": "user", "password": "pass"}
with sync_playwright() as p:
# opção A: um proxy para todo o navegador
browser = p.chromium.launch(proxy=PROXY)
page = browser.new_page()
page.goto("https://api.ipify.org?format=json")
print(page.text_content("body"))
browser.close()
# opção B: proxy diferente por contexto (um navegador, muitas identidades)
browser = p.chromium.launch(proxy={"server": "per-context"}) # espaço reservado; proxies reais vêm dos contextos
ctx_de = browser.new_context(proxy={"server": "http://gate.provider.com:7777",
"username": "customer-country-de-session-a1", "password": "pass"},
locale="de-DE", timezone_id="Europe/Berlin")
ctx_us = browser.new_context(proxy={"server": "http://gate.provider.com:7777",
"username": "customer-country-us-session-b2", "password": "pass"},
locale="en-US", timezone_id="America/New_York")
for ctx in (ctx_de, ctx_us):
pg = ctx.new_page(); pg.goto("https://api.ipify.org?format=json"); print(pg.text_content("body"))
browser.close()
Dois detalhes. Proxies por contexto no Chromium exigem que o navegador seja iniciado com uma opção de proxy (qualquer valor — o espaço reservado documentado é {"server": "per-context"}); sem isso, os contextos não podem substituir. E observe locale e timezone_id definidos junto com o proxy: um IP alemão com um fuso horário americano é uma contradição que os sistemas anti-bot ponderam, como explicado em como funcionam os sistemas anti-bot. Proxies SOCKS5 são suportados ("server": "socks5://host:1080"), mas o Chromium não envia credenciais para proxies SOCKS, então use autenticação por lista de permissão de IP com SOCKS.
Node.js
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ proxy: { server: 'per-context' } });
const context = await browser.newContext({
proxy: { server: 'http://gate.provider.com:7777',
username: 'customer-country-de-session-a1', password: 'pass' },
locale: 'de-DE', timezoneId: 'Europe/Berlin',
});
const page = await context.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.textContent('body'));
await browser.close();
})();
O campo bypass do Playwright ("bypass": "localhost,*.internal") exclui hosts do proxy, e launch_persistent_context() aceita a mesma opção proxy se você precisar de um diretório de perfil persistente.
Puppeteer
O Puppeteer passa o proxy como uma flag do Chromium e autentica por página:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
args: ['--proxy-server=http://proxy.example.com:8080'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'user', password: 'pass' }); // responde ao 407 para esta página
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
})();
page.authenticate() deve ser chamado antes da navegação e em todas as páginas (abas) que usarão o proxy. O proxy é para todo o navegador — o Puppeteer não tem configuração de proxy por contexto como o Playwright. Para várias identidades, você tem três opções: iniciar vários processos de navegador (um proxy cada; pesado, mas simples), usar --proxy-server apontando para um proxy de comutação local que roteia por algum marcador, ou mudar para o Playwright. Também é útil: --proxy-bypass-list=<-loopback> se você precisar que o localhost também seja proxyado, e puppeteer-extra com seu plugin de proxy, que lida com autenticação e pode rotacionar por página.
Selenium
O Selenium define o proxy por meio das opções do navegador. Proxies não autenticados (com lista de permissão de IP) são uma linha; os autenticados são o caso complicado.
Chrome, autenticação por lista de permissão
from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://proxy.example.com:8080")
driver = webdriver.Chrome(options=opts)
driver.get("https://api.ipify.org?format=json")
print(driver.find_element("tag name", "body").text)
driver.quit()
Chrome, nome de usuário/senha
O protocolo WebDriver do Selenium não tem gancho para responder ao 407 do proxy. As soluções alternativas padrão:
- Selenium Wire (Python): uma extensão do Selenium que roteia o tráfego por meio de seu próprio proxy local e suporta upstreams autenticados:
Observe que o Selenium Wire não é mais mantido ativamente; ainda funciona com o Chrome atual para muitos, mas trate-o como uma solução temporária.from seleniumwire import webdriver # pip install selenium-wire opts = {"proxy": {"http": "http://user:pass@proxy.example.com:8080", "https": "http://user:pass@proxy.example.com:8080"}} driver = webdriver.Chrome(seleniumwire_options=opts) - Uma extensão Chrome gerada que define o proxy via
chrome.proxye responde aonAuthRequiredcom as credenciais — um padrão bem conhecido; a extensão é um manifesto mais algumas linhas de script em segundo plano, carregada comopts.add_extension(...). Não funciona no modo headless com Chrome mais antigo (extensões não eram suportadas lá); funciona no headless=new atual. - Um proxy de encaminhamento local em
127.0.0.1que contém as credenciais e encaminha para o upstream; aponte o Chrome para ele sem autenticação. Independente de framework e robusto. - Autenticação por lista de permissão do provedor, se sua máquina tiver um IP estático — contorna o problema completamente.
Firefox
opts = webdriver.FirefoxOptions()
opts.set_preference("network.proxy.type", 1)
opts.set_preference("network.proxy.http", "proxy.example.com"); opts.set_preference("network.proxy.http_port", 8080)
opts.set_preference("network.proxy.ssl", "proxy.example.com"); opts.set_preference("network.proxy.ssl_port", 8080)
opts.set_preference("network.proxy.socks_remote_dns", True) # DNS remoto se usar SOCKS
driver = webdriver.Firefox(options=opts)
O Firefox ainda exibirá um diálogo de autenticação para proxies com nome de usuário/senha; as abordagens de encaminhador local ou lista de permissão se aplicam.
O que o proxy não resolve em um navegador headless
Um navegador real permite superar a impressão digital de TLS e HTTP/2 — o handshake é genuinamente do Chrome (veja impressão digital TLS). Isso não, por si só, permite superar a camada JavaScript:
navigator.webdriverétruesob automação; os sites verificam isso primeiro.- O modo headless tem diferenças de renderização e API (plugins ausentes, strings específicas do renderizador WebGL, dimensões de janela) que os scripts de impressão digital testam. O headless mais novo do Chrome (
headless=new) é muito mais próximo do headful, mas não idêntico. - WebRTC ainda pode expor o IP real da máquina, a menos que seja desabilitado ou falsificado; um proxy não o toca. Teste — Como testar um proxy.
- Fuso horário, localidade e geolocalização vêm da máquina, a menos que você os defina (Playwright:
timezone_id,locale,geolocation); uma incompatibilidade com o país do proxy é um sinal clássico. - Comportamento: navegações instantâneas, sem movimento do mouse, tempo idêntico a cada execução.
Plugins de stealth (puppeteer-extra-plugin-stealth, playwright-stealth) corrigem os artefatos mais óbvios; os navegadores antidetect vão além, fornecendo uma impressão digital coerente por perfil e expondo uma porta de depuração à qual seu script Playwright/Puppeteer se conecta — que é a resposta de produção para trabalho com contas. Veja o que é um navegador antidetect e, para escolher o tipo de IP por trás de cada perfil, nosso ranking de proxies para antidetect.
Escolhendo o framework para trabalho com proxy
| Playwright | Puppeteer | Selenium | |
|---|---|---|---|
| Proxy autenticado | Nativo (campos username/password) | page.authenticate() por página | Solução alternativa necessária (Wire, extensão, encaminhador local) |
| Proxy diferente por sessão | Por contexto, um navegador | Um navegador por proxy | Um driver por proxy |
| SOCKS5 | Sim (sem credenciais → lista de permissão) | Via flag (sem credenciais) | Via opções/preferências |
| Localidade / fuso horário por sessão | Opções de contexto nativas | APIs de emulação / flags | Preferências / CDP |
| Melhor para | Raspagem e testes com múltiplas identidades | Automação Chrome centrada em Node | Suites legadas, grades de navegadores cruzados |
Perguntas frequentes
Como uso um proxy autenticado no Playwright?
Passe proxy={"server": "http://host:port", "username": "…", "password": "…"} para launch() ou, por contexto, para new_context() (inicie com proxy={"server": "per-context"} primeiro).
Como uso um proxy com autenticação no Puppeteer?
Inicie com args: ['--proxy-server=host:port'] e chame await page.authenticate({username, password}) antes de page.goto() em cada página.
Por que o Chrome ignora user:pass@ em --proxy-server?
A flag do Chromium aceita apenas scheme://host:port; as credenciais não são analisadas. Ele responde ao 407 do proxy com um diálogo interativo, então a automação deve fornecer as credenciais por meio do mecanismo do framework.
Posso usar um proxy diferente para cada aba?
Não por aba. O Playwright faz isso por contexto do navegador (cada contexto é uma sessão isolada); Puppeteer e Selenium precisam de uma instância de navegador por proxy, ou um proxy de comutação local.
Um proxy torna o Chrome headless indetectável?
Não. Ele corrige a camada de IP e, sendo um navegador real, a camada TLS. Flags de automação, artefatos headless, WebRTC, incompatibilidade de fuso horário e comportamento são sinais separados que precisam de tratamento separado.
O proxy deve ser residencial ou datacenter para automação de navegador?
O que a política de IP do alvo exigir — o navegador não muda esse cálculo. Datacenter onde for tolerado; residencial ou móvel em sites protegidos; estático (ISP) para perfis logados persistentes. Veja a comparação de tipos de proxy.