---
title: "Proxy no Puppeteer: autenticação, rotação e SOCKS5"
description: "O Puppeteer usa um proxy pela flag --proxy-server do Chrome ou por contexto de navegador, com page.authenticate() para o login. Exemplos testados e erros."
url: https://proxynet.io/pt-br/blog/puppeteer-proxy
date: 2026-10-06
author: "Acar Diveroli"
category: "Integração, Web scraping"
lang: pt-BR
---

# Proxy no Puppeteer: autenticação, rotação e SOCKS5

Você adiciona `--proxy-server=http://user:pass@host:port` aos argumentos de inicialização, do jeito que escreveria um proxy para o cURL, e o primeiro `page.goto()` para com `net::ERR_NO_SUPPORTED_PROXIES`. Você tira as credenciais, e o erro passa a ser `net::ERR_INVALID_AUTH_CREDENTIALS`. O Puppeteer não tem uma opção de proxy própria. Ele inicia o Chrome, e é o Chrome que decide como um proxy é usado; por isso a maioria das respostas está nas regras do Chrome, e não na API do Puppeteer.

Este artigo cobre a flag de inicialização e o `page.authenticate()`, um proxy por contexto de navegador, sessões fixas e rotativas, SOCKS5, a lista de bypass, a verificação do IP de saída, os erros que você vai encontrar e um script completo com novas tentativas. Todos os exemplos rodaram em 6 de outubro de 2026 com o Puppeteer 25.12.0 e a versão 154 do Chrome que ele baixa, contra um proxy HTTP local que exige usuário e senha e contra um servidor SOCKS5 local. As mensagens de erro citadas são as que obtivemos.

> **Nota: Resposta rápida**
>
> O Puppeteer entrega o proxy ao Chrome: coloque `--proxy-server=http://host:port` nos `args` de `puppeteer.launch()`, ou passe `proxyServer` para `browser.createBrowserContext()` para dar a um contexto o seu próprio proxy. O usuário e a senha nunca entram nesse endereço; chame `page.authenticate({ username, password })` antes do primeiro `page.goto()`. Depois disso, o Chrome reutiliza essas credenciais no contexto inteiro, então uma sessão de proxy significa um contexto. O SOCKS5 só funciona sem senha, e por isso é combinado com uma whitelist de IP.

## Como o Puppeteer envia o tráfego por um proxy?

O Puppeteer é uma biblioteca Node.js que controla um navegador real a partir do código. Ele não abre conexões de rede por conta própria; quem faz isso é o navegador. Por isso o proxy é uma configuração do Chrome, passada como flag de linha de comando quando o navegador inicia ou como opção quando um contexto de navegador é criado. Um contexto de navegador é uma sessão isolada dentro de um mesmo navegador, com cookies, cache e armazenamento próprios, parecida com uma janela anônima.

O login no proxy é uma etapa separada, porque o Chrome não lê credenciais do endereço do proxy. Nosso proxy de teste registrou esta sequência para uma página HTTPS:

1. O Chrome envia `CONNECT httpbin.org:443`, pedindo ao proxy que abra um túnel até o site. Para uma página `http://`, ele envia a própria requisição.
2. A requisição não leva credenciais, então o proxy responde `407 Proxy Authentication Required`. A [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110#status.407) define o 407 como o equivalente do 401 do lado do proxy.
3. O Puppeteer captura esse desafio e responde com o usuário e a senha passados ao `page.authenticate()`.
4. O Chrome repete a requisição com um cabeçalho `Proxy-Authorization`, o proxy responde `200`, e a conexão criptografada com o site é montada dentro do túnel. O proxy vê o nome do host, não o conteúdo da página.
5. O Chrome guarda as credenciais no cache de autenticação do contexto e as envia de imediato nas conexões seguintes.

A etapa 5 explica por que as credenciais acabam pertencendo ao contexto, como mostram os testes mais abaixo.

## Como configurar um proxy com --proxy-server e page.authenticate()?

`npm i puppeteer` instala a biblioteca e baixa um Chrome compatível; `puppeteer-core` é a mesma API sem esse download. A configuração mínima:

```js
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  args: ["--proxy-server=http://pr.proxynet.io:8000"],
});
const page = await browser.newPage();
await page.authenticate({ username: "user", password: "pass" });

await page.goto("https://httpbin.org/ip");
console.log(await page.$eval("body", (el) => el.innerText)); // o IP de saída que o site vê

await browser.close();
```

O `http://` na flag descreve a conexão com o proxy, não as páginas. Um proxy HTTP também leva sites `https://` pelo túnel, e o conteúdo deles continua criptografado entre o Chrome e o site. A [referência de `page.authenticate()`](https://pptr.dev/api/puppeteer.page.authenticate) acrescenta que, para isso, o Puppeteer liga nos bastidores a interceptação de requisições (request interception), o que pode custar um pouco de desempenho; passar `null` a desliga.

Três coisas deram errado nos nossos testes, e cada uma virou uma regra fixa:

- **Chame `page.authenticate()` antes do primeiro `goto()`.** Quando chamado depois, a primeira navegação falhou com `net::ERR_INVALID_AUTH_CREDENTIALS`; só a seguinte passou.
- **Deixe as credenciais fora da flag.** Com `user:pass@` no endereço, o Chrome 154 rejeitou o valor inteiro com `net::ERR_NO_SUPPORTED_PROXIES`. Guias antigos descrevem um `407` nesse ponto; hoje a requisição nem chega ao proxy.
- **Não envie `Proxy-Authorization` por conta própria.** Ao definir esse cabeçalho com `page.setExtraHTTPHeaders()`, o Chrome recusou a navegação com `net::ERR_INVALID_ARGUMENT`.

| Valor de `--proxy-server` | Resultado no Chrome 154 |
|---|---|
| `http://pr.proxynet.io:8000` | Funciona; páginas HTTPS passam por um túnel `CONNECT` |
| `pr.proxynet.io:8000` | Funciona; sem esquema, o Chrome assume um proxy HTTP |
| `http://user:pass@pr.proxynet.io:8000` | `net::ERR_NO_SUPPORTED_PROXIES` |
| `socks5://host:port` | Só funciona quando o servidor não pede senha |
| `socks5h://host:port` | `net::ERR_NO_SUPPORTED_PROXIES`; o Chrome não conhece esse esquema |

## Como usar um proxy diferente em cada contexto de navegador?

O Puppeteer 22 renomeou `createIncognitoBrowserContext()`, o nome que guias antigos ainda usam, para `browser.createBrowserContext()`. As suas [`BrowserContextOptions`](https://pptr.dev/api/puppeteer.browsercontextoptions) trazem dois campos de proxy, `proxyServer` e `proxyBypassList`. O navegador pode iniciar sem proxy enquanto cada contexto recebe o seu:

```js
import puppeteer from "puppeteer";

const PROXY = "http://pr.proxynet.io:8000";
// Uma sessão fixa por contexto: mesmo ID de sessão -> mesmo IP de saída por até 600 segundos
const sessions = ["a1b2c3", "d4e5f6"];

const browser = await puppeteer.launch(); // o navegador em si inicia sem proxy
for (const id of sessions) {
  const context = await browser.createBrowserContext({ proxyServer: PROXY });
  const page = await context.newPage();
  await page.authenticate({ username: `user-session-${id}-ttl-600`, password: "pass" });

  for (let i = 0; i < 2; i++) {
    await page.goto("https://httpbin.org/ip");
    console.log(id, await page.$eval("body", (el) => el.innerText));
  }
  await context.close(); // cookies, cache e a configuração de proxy vão embora com o contexto
}
await browser.close();
```

Nosso proxy de teste distribui as saídas por ID de sessão, como faz um gateway. Os dois contextos saíram por dois endereços diferentes, e cada um manteve o seu endereço na segunda requisição. Mais dois resultados importam em trabalhos reais:

- **As credenciais pertencem ao contexto, não à página.** Uma segunda página no mesmo contexto chamou `page.authenticate()` com outro usuário, e mesmo assim todas as requisições dela continuaram levando o primeiro usuário. Uma página que nunca fez essa chamada passou com as mesmas credenciais em cache.
- **Um contexto sem `proxyServer` herda o proxy da flag de inicialização, mas mantém as próprias credenciais.** Com `--proxy-server` no navegador, dois contextos com IDs de sessão próprios ainda saíram por duas saídas diferentes.

| | Flag `--proxy-server` | `createBrowserContext({ proxyServer })` |
|---|---|---|
| Alcance | O navegador inteiro | Só aquele contexto |
| Saídas separadas em um navegador | Sim, um usuário por contexto | Sim, até hosts de proxy diferentes |
| Trocar o proxy | Reiniciar o navegador | Fechar o contexto e abrir outro |
| Indicado para | Um script, uma saída | Muitas sessões independentes em paralelo |

A regra prática: uma sessão, um contexto, um usuário.

## Rotativa ou fixa: qual tipo de sessão combina com um navegador?

Um navegador não envia uma requisição por página. Uma página de produto puxa o documento HTML, scripts, folhas de estilo, imagens e chamadas de API em segundo plano, muitas vezes de vários hosts e por várias conexões. Em um gateway rotativo, cada nova conexão pode receber um novo IP de saída; no nosso teste, até o favicon de uma página saiu por um endereço diferente do da página. Para páginas sem relação entre si, isso não faz mal. Já em um login, um carrinho ou um formulário de várias etapas, o site vê um visitante pulando entre endereços e pode encerrar a sessão.

O gateway lê o tipo de sessão a partir do nome de usuário, que o Gerador de endpoints do painel escreve para você. As partes são fáceis de ler: `-country-de` escolhe o país de saída (a cidade é escolhida no mesmo gerador), `-session-a1b2c3-ttl-600` mantém uma saída para esse ID de sessão pelo número de segundos indicado, de 1 a 60 minutos, e um usuário sem a parte de sessão faz rotação.

Para páginas independentes, como listas de categorias ou resultados de busca abertos em contextos de vida curta, use os [Proxies rotativos](https://proxynet.io/pt-br/rotating-proxy).

Para tudo o que guarda estado, use os [Proxies de sessão fixa](https://proxynet.io/pt-br/sticky-proxy): um contexto, um ID de sessão, mantido por mais tempo do que o fluxo leva. Quando o trabalho terminar, feche o contexto para que os cookies e o IP terminem juntos.

A rotação distribui trabalho independente entre saídas; ela não é um jeito de contornar os limites de um site. Um `429 Too Many Requests` ou um `403 Forbidden` pede que você diminua o ritmo, e um IP novo não muda essa resposta.

## O Puppeteer funciona com proxy SOCKS5?

Sim, mas sem usuário e senha. A [documentação de proxy do Chromium](https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md) afirma que nenhum método de autenticação é suportado para SOCKSv5. Nosso servidor SOCKS5 local viu isso do outro lado: a saudação do Chrome oferecia apenas o método `0x00`, "sem autenticação". Um servidor que exigia o método de usuário e senha (`0x02`) tinha de recusar essa oferta, e a navegação falhava com `net::ERR_SOCKS_CONNECTION_FAILED`. O `page.authenticate()` não fez diferença, já que o SOCKS5 não tem um desafio no estilo do 407 para o Puppeteer responder.

A solução é provar quem você é pelo endereço IP. Adicione o IP público da máquina que roda o Chrome à whitelist de IP no painel (até 10 endereços), pegue a porta SOCKS5 que o Gerador de endpoints mostra e inicie o navegador sem credenciais:

```js
import puppeteer from "puppeteer";

// Sem usuário nem senha: o IP público desta máquina precisa estar na whitelist de IP.
// SOCKS5_PORT: a porta SOCKS5 mostrada no Gerador de endpoints.
const { SOCKS5_PORT } = process.env;

const browser = await puppeteer.launch({
  args: [`--proxy-server=socks5://pr.proxynet.io:${SOCKS5_PORT}`],
});
const page = await browser.newPage();
await page.goto("https://httpbin.org/ip");
console.log(await page.$eval("body", (el) => el.innerText));
await browser.close();
```

O Chrome enviou nomes de host ao servidor SOCKS5, e não endereços IP; assim o DNS é resolvido no proxy, e o hábito de `socks5h://` vindo do cURL não é necessário. Para páginas web, um proxy HTTP costuma ser a escolha mais simples, porque funciona com `page.authenticate()`.

## Como deixar alguns endereços fora do proxy?

A flag `--proxy-bypass-list`, ou `proxyBypassList` como array em um contexto, lista os hosts que o Chrome acessa diretamente. Na flag, as regras são separadas por ponto e vírgula ou por vírgula: `--proxy-bypass-list=*.internal.example;192.168.0.0/16`. No nosso teste, um host da lista ignorou o proxy por completo e foi resolvido na máquina local.

O Chrome também tem regras implícitas: `localhost`, `*.localhost`, `127.0.0.1/8` e `[::1]` nunca usam o proxy, mesmo com a lista vazia. Se você testa contra um servidor local e o log do proxy fica em silêncio, o motivo é esse. A regra especial `<-loopback>` remove essas regras implícitas; com ela, nossa requisição para `127.0.0.1` passou pelo proxy.

## Como verificar o IP de saída?

Compare, no mesmo navegador, um contexto direto com um contexto que passa pelo proxy. Se os endereços forem diferentes, o tráfego está passando pelo proxy:

```js
import puppeteer from "puppeteer";

async function exitIp(context, credentials) {
  const page = await context.newPage();
  if (credentials) await page.authenticate(credentials);
  await page.goto("https://httpbin.org/ip");
  const { origin } = JSON.parse(await page.$eval("body", (el) => el.innerText));
  await page.close();
  return origin;
}

const browser = await puppeteer.launch();
const direct = await browser.createBrowserContext();
const proxied = await browser.createBrowserContext({ proxyServer: "http://pr.proxynet.io:8000" });

const own = await exitIp(direct);
const viaProxy = await exitIp(proxied, { username: "user", password: "pass" });
console.log({ own, viaProxy, proxyWorks: own !== viaProxy });
await browser.close();
```

Se o seu alvo é um país, consulte também o IP de saída em um serviço de geolocalização. Quando a verificação falhar, tire o Chrome da equação: [Seu proxy está funcionando? Como testar um proxy](/pt-br/blog/how-to-test-a-proxy) traz os testes de linha de comando que separam um problema de rede de um problema de código.

## Quais erros você vai ver e o que eles significam?

Provocamos cada um deles no ambiente de teste local:

| O que você vê | O que aconteceu | O que fazer |
|---|---|---|
| `net::ERR_PROXY_CONNECTION_FAILED` | O Chrome não alcançou o proxy: host ou porta errados, ou o tráfego de saída está bloqueado | Confira o endereço e a porta; tentar de novo não ajuda |
| `net::ERR_INVALID_AUTH_CREDENTIALS` | O proxy pediu login e o Chrome não tinha credenciais | Chame `page.authenticate()` antes do primeiro `goto()` |
| `goto()` resolve com status `407` | A senha estava errada; o Puppeteer tentou uma vez e desistiu | Confira `response.status()` e corrija as credenciais |
| `net::ERR_TUNNEL_CONNECTION_FAILED` | O login funcionou, mas o proxy não conseguiu abrir o túnel até o site | Confira o endereço de destino |
| `net::ERR_NO_SUPPORTED_PROXIES` | O Chrome rejeitou o valor de `--proxy-server` | Remova `user:pass@`, use `http://` ou `socks5://` |
| `net::ERR_SOCKS_CONNECTION_FAILED` | O servidor SOCKS5 quer senha, ou o seu IP não está na whitelist | Coloque o seu IP na whitelist ou use a porta HTTP |
| A página carrega, mas o IP é o seu | Uma regra de bypass ou um host de loopback ignorou o proxy | Confira a lista de bypass e o `<-loopback>` |

A linha da senha errada é a que fica escondida por mais tempo: nada lança exceção, e o script segue em frente com a página 407 do proxy. Em um navegador de desktop, o erro de túnel tem mais causas, da verificação HTTPS do antivírus a um proxy do sistema esquecido; elas estão em [Como resolver err_tunnel_connection_failed no Chrome e Edge](/pt-br/blog/err-tunnel-connection-failed). Durante o desenvolvimento, dois listeners mostram o erro real por trás de um tempo esgotado:

```js
page.on("requestfailed", (req) => console.log("FALHOU", req.url(), req.failure()?.errorText));
page.on("response", (res) => res.status() >= 400 && console.log(res.status(), res.url()));
```

## Exemplo completo: contextos em paralelo, sessões fixas e novas tentativas

O script abre seis páginas geradas com JavaScript, com no máximo três contextos ao mesmo tempo. Cada tentativa recebe um contexto novo e uma sessão fixa nova, de modo que os cookies e o IP de saída mudam juntos. Imagens, mídia e fontes são bloqueadas para economizar tráfego; `page.setRequestInterception()` faz isso sem quebrar o `page.authenticate()`. Tempos esgotados e falhas de túnel são repetidos com espera exponencial, enquanto erros de configuração e um `403` ou `429` do site encerram o trabalho.

```js
import puppeteer from "puppeteer";

const PROXY = "http://pr.proxynet.io:8000";
const USER = "user";
const PASS = "pass";
const URLS = Array.from({ length: 6 }, (_, i) => `https://quotes.toscrape.com/js/page/${i + 1}/`);
const CONCURRENCY = 3; // contextos abertos ao mesmo tempo
const ATTEMPTS = 3;
const BLOCKED = new Set(["image", "media", "font"]);
// Erros de configuração: esperar e tentar de novo não resolve
const FATAL = ["ERR_PROXY_CONNECTION_FAILED", "ERR_NO_SUPPORTED_PROXIES", "ERR_INVALID_AUTH_CREDENTIALS"];

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function scrape(browser, url) {
  for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
    // Um contexto por tentativa: cookies próprios e sessão fixa própria (um IP de saída)
    const context = await browser.createBrowserContext({ proxyServer: PROXY });
    try {
      const page = await context.newPage();
      const session = crypto.randomUUID().slice(0, 8);
      await page.authenticate({ username: `${USER}-session-${session}-ttl-300`, password: PASS });
      await page.setRequestInterception(true);
      page.on("request", (req) => (BLOCKED.has(req.resourceType()) ? req.abort() : req.continue()));

      const res = await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30_000 });
      const status = res ? res.status() : 0;
      if (status === 403 || status === 429) {
        // O site está recusando ou freando você: um IP novo não é a resposta
        throw Object.assign(new Error(`HTTP ${status}: pare e reduza o ritmo de requisições`), { fatal: true });
      }
      if (status >= 400) throw new Error(`HTTP ${status}`);

      await page.waitForSelector("div.quote span.text", { timeout: 10_000 }); // gerado com JavaScript
      return await page.$$eval("div.quote span.text", (els) => els.map((el) => el.textContent));
    } catch (err) {
      err.fatal ||= FATAL.some((code) => err.message.includes(code));
      if (err.fatal || attempt === ATTEMPTS) throw err;
      await sleep(2 ** attempt * 1000 + Math.random() * 1000); // espera antes da próxima tentativa
    } finally {
      await context.close();
    }
  }
}

const browser = await puppeteer.launch();
const queue = [...URLS];
const results = new Map();
await Promise.all(
  Array.from({ length: CONCURRENCY }, async () => {
    while (queue.length) {
      const url = queue.shift();
      try {
        results.set(url, `${(await scrape(browser, url)).length} citações`);
      } catch (err) {
        results.set(url, `ERRO ${err.message.split("\n")[0]}`);
        if (err.fatal) queue.length = 0; // para o trabalho inteiro, não só esta página
      }
    }
  }),
);
await browser.close();
for (const url of URLS) console.log(url, results.get(url) ?? "ignorada");
```

Pelo proxy de teste local, as seis páginas voltaram com dez citações cada uma em menos de cinco segundos. Com a porta do proxy digitada errada de propósito, as três páginas em andamento pararam na hora com `net::ERR_PROXY_CONNECTION_FAILED`, e as outras três foram ignoradas; uma página de teste local que respondia `429` encerrou o trabalho da mesma forma. Comece com um valor baixo de `CONCURRENCY`: cada contexto ocupa memória, e o site de destino também tem limites.

## Casos de uso

- Páginas de catálogo e de preços geradas com JavaScript, que um cliente HTTP simples vê vazias.
- Verificar como o seu próprio site, os seus preços ou os seus banners de consentimento aparecem em outro país, com um contexto por país.
- Capturas de tela e PDFs de páginas públicas do jeito que um visitante de determinado país as vê.
- Monitorar as suas próprias páginas e fluxos de checkout de fora da sua rede.

A mesma moldura vale para todos: respeite o `robots.txt` e os termos do site, use uma API oficial quando ela existir e mantenha o ritmo de requisições em um nível que o site aguente.

## Erros comuns

- **Escrever `user:pass@` em `--proxy-server`.** O Chrome rejeita o valor com `net::ERR_NO_SUPPORTED_PROXIES`.
- **Esperar um usuário novo por página.** O Chrome mantém as primeiras credenciais para o contexto inteiro; use um contexto por sessão.
- **Tentar uma senha com SOCKS5.** O Chrome oferece só "sem autenticação"; use a whitelist de IP ou a porta HTTP.
- **Testar contra `localhost` e confiar no resultado.** Endereços de loopback ignoram o proxy, a menos que você adicione `<-loopback>`.
- **Tratar uma resposta `407` como se fosse uma página.** Uma senha errada não lança exceção; confira `response.status()`.
- **Trocar o IP depois de um `429` ou `403`.** Diminua o ritmo; o limite tem a ver com o seu comportamento, não com o seu endereço.
- **Deixar contextos abertos.** Cada um ocupa memória; feche-os em um bloco `finally`.

## Guia de decisão

| Necessidade | Recomendação |
|---|---|
| Um script, uma saída | Flag `--proxy-server` mais `page.authenticate()` |
| Muitas sessões independentes em um navegador | `createBrowserContext({ proxyServer })`, um ID de sessão por contexto |
| Muitas páginas que não dependem umas das outras | Usuário rotativo, contextos de vida curta |
| Login, carrinho ou formulário de várias etapas | Sessão fixa mais longa que o fluxo, um contexto |
| SOCKS5 é obrigatório | Whitelist de IP, sem credenciais |
| Uma ferramenta que só aceita a flag de inicialização | Whitelist de IP ou um encaminhador local como `proxy-chain` |
| Hosts internos precisam ficar diretos | `--proxy-bypass-list` ou `proxyBypassList` |

## Perguntas frequentes

### O puppeteer.launch() tem uma opção de proxy?

Não. O proxy vai em `args` como a flag `--proxy-server` do Chrome, e o login vai em `page.authenticate()`. A API do próprio Puppeteer só tem campos de proxy em `browser.createBrowserContext()`: `proxyServer` e `proxyBypassList`.

### Cada página pode ter o seu próprio proxy?

Não dentro de um mesmo contexto. O proxy e as credenciais em cache pertencem ao contexto, então no nosso teste uma segunda página com outras credenciais continuou usando as primeiras. Dê a cada página que precisa de uma saída própria o seu próprio contexto; um contexto custa muito menos que um navegador novo.

### O que é o proxy-chain e você precisa dele?

`proxy-chain` é um pacote Node.js de código aberto cuja função `anonymizeProxy()` inicia um proxy local sem senha e encaminha o tráfego para um proxy upstream com credenciais, de modo que o Chrome recebe um endereço simples `http://127.0.0.1:<port>`. No nosso teste, funcionou tanto com um upstream HTTP quanto com um SOCKS5 que exigiam senha. Com `page.authenticate()` ou uma whitelist de IP, você não precisa dele.

### Dá para trocar o proxy sem reiniciar o navegador?

Sim, por meio de contextos. Feche o contexto, abra um novo com outro `proxyServer` ou outro ID de sessão e chame `page.authenticate()` de novo. Só a flag `--proxy-server` fica fixa durante toda a vida do navegador.

### Por que o log do proxy mostra requisições que você não fez?

O tráfego de fundo do próprio Chrome, como verificações de atualização e requisições a serviços do Google, vai para o mesmo proxy. No nosso log, a maior parte chegou sem credenciais e recebeu um `407`, porque `page.authenticate()` responde apenas aos desafios do tráfego das páginas. Isso não afeta as suas páginas.

### Um proxy acaba com os CAPTCHAs no Puppeteer?

Não. Um proxy muda de onde o tráfego vem, enquanto os CAPTCHAs também reagem ao ritmo de requisições, aos sinais do navegador e a sessões inconsistentes. As causas e as formas legítimas de reduzi-los estão em [Puppeteer e CAPTCHA: por que aparece, como reduzir?](/pt-br/blog/puppeteer-captcha).

## Em resumo

No Puppeteer, o proxy é uma configuração do Chrome: a flag `--proxy-server` para o navegador inteiro ou `proxyServer` para um contexto, com o login em `page.authenticate()` antes da primeira navegação. As credenciais ficam em cache por contexto, então monte cada sessão como um contexto, escolha sessão fixa ou rotativa pelo nome de usuário e use uma whitelist de IP para SOCKS5. Confira `response.status()` e mantenha um ritmo de requisições educado. Para páginas que devem abrir a partir de conexões domésticas comuns, as saídas vêm de um pool de [Proxies residenciais](https://proxynet.io/pt-br/residential-proxy).
