No Python, configurar um proxy é um único parâmetro proxies na maioria das bibliotecas. No Node.js a coisa é mais confusa: o fetch nativo, o Axios e o node-fetch recebem o proxy por três mecanismos diferentes, o SOCKS5 precisa de um pacote separado e, quando você passa a opção errada para uma biblioteca, muitas vezes nem recebe erro; a requisição sai silenciosamente sem proxy. Por isso é importante saber qual biblioteca lê qual opção.
Neste artigo, vemos com código que funciona as três formas básicas de configurar proxy no Node.js (variável de ambiente, agent e dispatcher), o uso de um proxy com autenticação no fetch nativo, no Axios e no node-fetch, proxies SOCKS5 e a rotação de IP com novas tentativas. Rodamos os exemplos no Node.js 24 contra um proxy de teste local que exige usuário e senha, e anotamos a incompatibilidade de versões que encontramos e como os erros de autenticação aparecem de forma diferente em cada biblioteca.
Formas de configurar proxy no Node.js
O Node.js tem três mecanismos para enviar uma requisição HTTP por um proxy. A biblioteca que você usa define qual mecanismo vale.
- Variável de ambiente. As variáveis
HTTP_PROXY,HTTPS_PROXYeNO_PROXY. Elas fazem um script inteiro passar pelo proxy sem mudar o código, mas nem toda biblioteca as lê. - Agent. Os módulos clássicos
httpehttpsdo Node.js abrem conexões por um objetoAgent. O Axios e o node-fetch usam esses módulos, então, quando você passa a eles um agent que se conecta a um proxy (como oHttpsProxyAgent), as requisições passam pelo proxy. - Dispatcher. O
fetchnativo do Node.js não usa o módulo clássicohttp; usa um cliente chamado undici. No undici, o objeto que gerencia as conexões se chamadispatcher, e para proxy você passa umProxyAgent.
Essa distinção é o ponto mais importante do artigo: o fetch nativo não reconhece a opção agent. Se, por costume do Axios, você escrever fetch(url, { agent }), não recebe erro nenhum e a requisição sai sem proxy.
Explicamos como um proxy funciona em geral e o túnel CONNECT aberto em requisições HTTPS em O que é um servidor proxy e como ele funciona?. Traduzir comandos cURL para fetch e Axios (cabeçalhos, corpo, dados de formulário) é o assunto do nosso artigo cURL em JavaScript; este artigo foca só na parte do proxy.
Como usar proxy no fetch nativo?
ProxyAgent do undici
Instale o pacote undici:
npm install undiciDepois importe fetch e ProxyAgent do mesmo pacote:
import { fetch, ProxyAgent } from "undici";
const dispatcher = new ProxyAgent("http://user:pass@pr.proxynet.io:8000");
const res = await fetch("https://httpbin.org/ip", {
dispatcher,
signal: AbortSignal.timeout(20_000),
});
console.log(res.status, await res.json());Mesmo que o endereço do proxy comece com http://, você pode acessar sites HTTPS sem problema: o ProxyAgent abre um túnel CONNECT com o proxy, e a conexão TLS com o destino acontece dentro desse túnel.
A armadilha das versões. O Node.js traz a própria cópia do undici; o undici que você instala pelo npm pode ser mais novo. Quando passamos o ProxyAgent do npm ao fetch nativo do Node.js (usando o fetch global sem importar), a requisição falhou com o erro fetch failed no Node.js 24.11 com o undici 8.10; a causa era invalid onRequestStart method. Usar o mesmo agent com o fetch do próprio undici funcionou normalmente. A regra é simples: pegue o fetch do mesmo pacote de onde pegou o ProxyAgent.
Para que todas as chamadas fetch do undici na aplicação usem o mesmo proxy, você pode definir um dispatcher global:
import { fetch, ProxyAgent, setGlobalDispatcher } from "undici";
setGlobalDispatcher(new ProxyAgent("http://user:pass@pr.proxynet.io:8000"));
const res = await fetch("https://httpbin.org/ip"); // não precisa passar dispatcherA variável de ambiente NODE_USE_ENV_PROXY
Nas versões atuais do Node.js, o fetch nativo lê as variáveis de ambiente padrão de proxy quando NODE_USE_ENV_PROXY está ativada. Segundo o guia de configuração de rede corporativa do Node.js, o recurso está disponível a partir das versões 22.21.0 e 24.5.0; o mesmo comportamento pode ser ligado com a opção de linha de comando --use-env-proxy.
No Linux e no macOS:
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://user:pass@pr.proxynet.io:8000" node app.mjsNo Windows PowerShell:
$env:NODE_USE_ENV_PROXY = "1"
$env:HTTPS_PROXY = "http://user:pass@pr.proxynet.io:8000"
node app.mjsEsse método não exige mudança no código; fetch("https://httpbin.org/ip") passa direto pelo proxy. No nosso teste, quando o mesmo comando rodou sem NODE_USE_ENV_PROXY, a requisição não chegou ao proxy mesmo com HTTPS_PROXY definida. Você pode deixar endereços da rede interna fora do proxy com a variável NO_PROXY.
Se quiser um dispatcher que leia as variáveis de ambiente pelo código, a classe EnvHttpProxyAgent do undici faz o mesmo trabalho:
import { fetch, EnvHttpProxyAgent } from "undici";
const res = await fetch("https://httpbin.org/ip", { dispatcher: new EnvHttpProxyAgent() });Como usar proxy no Axios?
No Node.js, o Axios usa os módulos clássicos http e https e oferece duas formas de usar proxy.
A opção proxy nativa
A opção proxy da configuração de requisição do Axios funciona com endereços http:// sem criptografia:
import axios from "axios";
const { data } = await axios.get("http://httpbin.org/ip", {
proxy: {
protocol: "http",
host: "pr.proxynet.io",
port: 8000,
auth: { username: "user", password: "pass" },
},
timeout: 20_000,
});
console.log(data);A senha no campo auth não é codificada; você pode escrever os caracteres especiais como eles são.
https-proxy-agent para endereços HTTPS
Quando o endereço de destino é HTTPS, deixar um agent abrir o túnel dá resultados mais confiáveis:
npm install axios https-proxy-agentimport axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";
const httpsAgent = new HttpsProxyAgent("http://user:pass@pr.proxynet.io:8000");
const client = axios.create({
httpsAgent,
proxy: false, // desliga a lógica de proxy própria do Axios e deixa o túnel com o agent
timeout: 20_000,
});
const { status, data } = await client.get("https://httpbin.org/ip");
console.log(status, data);Não pule a linha proxy: false. Quando a opção proxy não é informada, o Axios pode tentar aplicar o proxy das variáveis de ambiente com a própria lógica, o que significa dois comportamentos de proxy em conflito junto com o agent. Vincular o agent uma vez com axios.create evita repeti-lo em cada chamada.
Como usar proxy no node-fetch?
Antes de o fetch nativo chegar, o node-fetch era a implementação de fetch mais comum no Node.js, e ainda aparece bastante em projetos antigos. Diferente do fetch nativo, ele usa o módulo clássico http, então o proxy é passado com a opção agent:
npm install node-fetch https-proxy-agentimport fetch from "node-fetch";
import { HttpsProxyAgent } from "https-proxy-agent";
const agent = new HttpsProxyAgent("http://user:pass@pr.proxynet.io:8000");
const res = await fetch("https://httpbin.org/ip", { agent });
console.log(res.status, await res.json());A versão 3 do node-fetch é publicada só como módulo ES; se você não consegue carregá-la com import em um projeto CommonJS que usa require, mudar para o fetch nativo geralmente dá menos trabalho. Não há motivo para adicionar o node-fetch a um projeto novo.
Como usar proxy SOCKS5?
O ProxyAgent do undici é para proxies HTTP; o fetch nativo não suporta SOCKS5. Para trabalhar com um proxy SOCKS5, use o pacote socks-proxy-agent com o Axios ou o node-fetch:
npm install socks-proxy-agentimport axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";
// socks5h: o nome de domínio é resolvido do lado do proxy, então nenhuma consulta DNS sai da sua rede
const agent = new SocksProxyAgent("socks5h://user:pass@pr.proxynet.io:1080");
const { data } = await axios.get("https://httpbin.org/ip", {
httpAgent: agent,
httpsAgent: agent,
proxy: false,
});
console.log(data);No node-fetch, o mesmo agent é passado como fetch(url, { agent }). Nos registros do nosso proxy de teste, as requisições enviadas com o esquema socks5h:// chegaram ao proxy como nome de domínio, não como endereço IP. O esquema socks5:// resolve o nome de domínio no seu computador; os problemas que isso causa estão em Vazamentos de WebRTC e DNS. Antes de escolher entre SOCKS5 e proxy HTTP, veja SOCKS ou HTTP proxy.
Como cada biblioteca recebe o proxy?
| Cliente | Como passar o proxy | Variável de ambiente | SOCKS5 | Erro de autenticação |
|---|---|---|---|---|
| Fetch nativo | NODE_USE_ENV_PROXY ou --use-env-proxy | Só com a flag | Não | fetch failed |
fetch do undici | dispatcher: new ProxyAgent(...) | Com EnvHttpProxyAgent | Não | fetch failed, causa: requisição cancelada |
| Axios | Opção proxy ou httpsAgent + proxy: false | Se nenhuma opção for passada | Com socks-proxy-agent | Erro com código de status 407 |
| node-fetch | agent | Não | Com socks-proxy-agent | Depende do agent usado |
http.request | agent | Não | Com socks-proxy-agent | Depende do agent usado |
A última coluna importa na depuração. No nosso teste com senha errada, o Axios mostrou a causa com clareza, com a mensagem Request failed with status code 407 e error.response.status === 407. O fetch do undici só deu um erro fetch failed e informou a causa como "Request was cancelled"; a mensagem não menciona autenticação. Se você vir fetch failed com o undici, a primeira coisa a conferir é o usuário e a senha do proxy. Reunimos todas as causas do 407 em Autenticação de proxy: user:pass ou whitelist de IP.
Credenciais e caracteres especiais
Em todos os métodos que recebem o endereço do proxy como URL (ProxyAgent do undici, https-proxy-agent, socks-proxy-agent, variáveis de ambiente), caracteres como @, :, / e # na senha precisam ser codificados. Senão, o endereço é interpretado errado.
const user = process.env.PROXY_USER;
const pass = encodeURIComponent(process.env.PROXY_PASS);
const proxyUrl = `http://${user}:${pass}@pr.proxynet.io:8000`;Ler as credenciais de variáveis de ambiente em vez de escrevê-las no código evita que a senha apareça onde o código for compartilhado. No campo proxy.auth do Axios, a senha é escrita sem codificação.
Rotação de IP e novas tentativas
Em um trabalho real de coleta de dados, as requisições falham de vez em quando: o ponto de saída do proxy não alcança o destino, o destino retorna 429 ou 503, ou a conexão dá timeout. O exemplo abaixo escolhe aleatoriamente entre vários proxies, tenta de novo uma requisição que falhou com backoff exponencial e limita, com lotes pequenos, o número de requisições rodando ao mesmo tempo:
import { fetch, ProxyAgent } from "undici";
const PROXIES = [
"http://user:pass@pr.proxynet.io:8000",
"http://user:pass@pr.proxynet.io:8001",
];
const agents = PROXIES.map((p) => new ProxyAgent(p));
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function getWithRetry(url, { attempts = 4, baseMs = 500 } = {}) {
let lastError;
for (let i = 0; i < attempts; i++) {
const dispatcher = agents[Math.floor(Math.random() * agents.length)];
try {
const res = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20_000) });
if (res.status === 429 || res.status >= 500) {
await res.body?.cancel();
throw new Error(`HTTP ${res.status}`);
}
return await res.text();
} catch (err) {
lastError = err;
await sleep(baseMs * 2 ** i + Math.random() * 250);
}
}
throw lastError;
}
async function crawl(urls, concurrency = 5) {
const results = [];
for (let i = 0; i < urls.length; i += concurrency) {
const batch = urls.slice(i, i + concurrency);
results.push(...(await Promise.allSettled(batch.map((u) => getWithRetry(u)))));
}
return results;
}
const urls = ["https://httpbin.org/ip", "https://httpbin.org/status/503", "https://example.com/"];
const results = await crawl(urls, 2);
results.forEach((r, i) =>
console.log(urls[i], r.status, r.status === "fulfilled" ? `${r.value.length} bytes` : r.reason.message),
);Três detalhes do código importam:
Promise.allSettledevita que uma requisição com falha em um lote pare as outras. ComPromise.all, um único503faria perder os resultados do lote inteiro. No exemplo, o endereço/status/503foi marcado comorejecteddepois de quatro tentativas, enquanto os outros endereços retornaram os resultados.res.body?.cancel()libera a conexão sem ler o corpo de uma resposta que vamos tentar de novo. Corpos não lidos podem lotar o pool de conexões.- O componente aleatório (
Math.random() * 250) evita que requisições que falharam no mesmo momento sejam tentadas de novo ao mesmo tempo e causem um novo acúmulo.
Em vez de gerenciar a lista de proxies você mesmo, se você usar um Proxies rotativos que dá um IP de saída diferente a cada conexão por um único endereço, o array PROXIES fica com um elemento e a rotação acontece do lado do provedor. Para trabalhos em que o mesmo IP precisa ser mantido durante a sessão (páginas logadas, fluxos de várias etapas), prefere-se um Proxies de sessão fixa.
Este exemplo não lê o cabeçalho Retry-After de uma resposta 429. Uma abordagem que respeita o cabeçalho e separa quais códigos de status não devem ser tentados de novo está em Códigos de status HTTP no web scraping. Como o valor de concorrência afeta de fato a velocidade está em Concorrência e paralelismo.
Como verificar se o proxy está funcionando?
A cada configuração nova, verifique se a requisição realmente passa pelo proxy. O jeito mais fácil é enviar uma requisição a um endereço que retorna o IP de saída, primeiro sem proxy e depois com ele:
import { fetch, ProxyAgent } from "undici";
const ip = async (options = {}) => (await (await fetch("https://api.ipify.org?format=json", options)).json()).ip;
console.log("sem proxy:", await ip());
console.log("com proxy:", await ip({ dispatcher: new ProxyAgent(process.env.HTTPS_PROXY) }));Se as duas linhas mostrarem o mesmo endereço, a requisição não está passando pelo proxy. A causa mais comum é passar agent para o fetch nativo ou usar um agent no Axios sem escrever proxy: false.
Erros comuns
- Passar
agentpara o fetch nativo. A opção é ignorada em silêncio. A chave certa no fetch édispatcher. - Passar o
ProxyAgentdo undici do npm para o fetch global. Quando as versões não batem, você recebe um errofetch failed; importe também ofetchdo undici. - Definir
HTTPS_PROXYe supor que o fetch nativo vai ler. SemNODE_USE_ENV_PROXYou--use-env-proxy, ele não lê. - Não escrever
proxy: falsecom um agent no Axios. Dois mecanismos de proxy entram em conflito. - Não codificar os caracteres especiais da senha. O endereço é interpretado errado e a autenticação falha.
- Resolver DNS localmente com
socks5://. Usesocks5h://para resolução remota. - Não definir timeout. Uma saída de proxy que não responde deixa uma requisição sem timeout esperando por minutos. Use
AbortSignal.timeoutno undici etimeoutno Axios. - Iniciar todas as requisições de uma vez com
Promise.all. Sobrecarrega tanto o seu próprio pool de conexões quanto o limite de taxa do site de destino.
Qual método escolher?
| Sua situação | Recomendação |
|---|---|
| Projeto novo, poucas dependências | fetch do undici + ProxyAgent |
| Passar um script existente pelo proxy sem mudar o código | NODE_USE_ENV_PROXY=1 + HTTPS_PROXY |
| O projeto já usa Axios | Axios + https-proxy-agent + proxy: false |
| Projeto antigo com node-fetch | node-fetch + agent |
| Proxy SOCKS5 | Axios ou node-fetch + socks-proxy-agent (socks5h://) |
| Um IP diferente a cada requisição | Proxy rotativo, endereço único |
| O mesmo IP durante a sessão | Proxy sticky |
| Mensagens de erro claras importam | Axios (mostra o 407 com o código de status) |
Nada disso vale para JavaScript rodando no navegador: o fetch do navegador não consegue mudar o proxy pelo código; o proxy é definido nas configurações do navegador ou do sistema operacional. Para a configuração no Windows, veja Configuração de proxy no Windows e no Chrome.
Perguntas frequentes
O fetch nativo do Node.js suporta proxy?
A partir do Node.js 22.21.0 e 24.5.0, ele lê as variáveis HTTP_PROXY e HTTPS_PROXY quando a variável de ambiente NODE_USE_ENV_PROXY=1 ou a opção --use-env-proxy é usada. Para definir o proxy por requisição pelo código, use o fetch e o ProxyAgent do undici.
O Axios lê a variável de ambiente HTTPS_PROXY?
No Node.js, quando a opção proxy não é informada, o Axios tenta usar o proxy das variáveis de ambiente. Para um comportamento claro e previsível, recomendamos definir o proxy com um agent e escrever proxy: false.
Posso usar um proxy diferente em cada requisição?
Sim. No undici, você pode passar um ProxyAgent diferente para cada chamada fetch, e no Axios um httpsAgent diferente. Em vez de criar agents a cada requisição, crie uma vez e reutilize, como no exemplo acima; cada agent novo abre o próprio pool de conexões.
Como manter os cookies ao usar proxy?
O fetch nativo e o undici não guardam cookies; você precisa ler o cabeçalho Set-Cookie e adicioná-lo à próxima requisição como cabeçalho Cookie. No Axios, dá para usar um cookie jar baseado em tough-cookie. Lembre-se de que, junto com os cookies, o endereço IP também deve se manter durante toda a sessão.
Como passar proxy no Puppeteer ou no Playwright?
Ferramentas de automação de navegador não recebem o proxy como as bibliotecas do Node.js; recebem como opção ao iniciar o navegador. Os agents deste artigo não afetam o navegador. Mostramos o lado do Puppeteer em Puppeteer e CAPTCHA.
Escolho JavaScript ou Python?
Proxies funcionam nas duas linguagens; no Python, a maioria das bibliotecas recebe o proxy com um único parâmetro, enquanto no Node.js o mecanismo muda conforme a biblioteca. A escolha geralmente depende da linguagem da equipe e da estrutura das páginas de destino. Comparamos as duas em Web scraping: JavaScript ou Python?, e para bibliotecas Python veja HTTPX, Requests ou AIOHTTP.
Em resumo
No Node.js, o proxy é passado de três formas diferentes conforme o cliente: um dispatcher do undici ou NODE_USE_ENV_PROXY para o fetch nativo, e um agent para o Axios e o node-fetch. Para destinos HTTPS, use o Axios com https-proxy-agent e proxy: false, e SOCKS5 com socks-proxy-agent e o esquema socks5h://. Pegue ProxyAgent e fetch do mesmo pacote, codifique a senha, defina timeout e rode as requisições em lotes pequenos de Promise.allSettled com lógica de novas tentativas. Planos para o seu trabalho de coleta de dados estão na nossa página de solução de extração de dados.




