Conhecimento

Como Usar Proxies Residenciais em Node.js com Axios e Got

Proxies em Node.js: por que o Axios precisa de https-proxy-agent e proxy:false, a opção agent do Got, o ProxyAgent do undici para fetch, e rotação geográfica por requisição.

Chris Collins

Chris Collins

29 de julho de 2026 · 9 min de leitura

Node.js é o runtime padrão para uma enorme quantidade de trabalho de scraping e automação: um event loop que absorve tranquilamente milhares de requisições simultâneas, um ecossistema de pacotes massivo e a mesma linguagem do início ao fim. Conectar um residential proxy a ele é questão de poucas linhas, mas os detalhes pegam as pessoas de um jeito específico do Node, porque o cliente HTTP mais popular, Axios, tem uma opção de proxy que não faz o que você espera sobre HTTPS. Acerte o agente e o resto é fácil.

Este é o capítulo de Node.js na mesma série de residential proxies with Python, with Playwright, e in Go: o código que funciona para Axios, Got e o fetch nativo, além das armadilhas que são exclusivas do ecossistema Node.

Tudo abaixo usa o gateway residencial da Shifter: um único endpoint, p.shifter.io:443, com toda a segmentação codificada no username. Troque o host e as credenciais para outro provedor; a estrutura é a mesma.

O modelo de gateway em um parágrafo

O username do proxy carrega sua autenticação e sua segmentação. Você não troca de endpoint para mudar de país ou sessão, você muda a string do username:

customer-USERNAME-country-us-sid-abc123-ttl-600

country-us direciona para os Estados Unidos, sid fixa uma sessão persistente, ttl mantém esse IP por N segundos. Omita sid/ttl e cada nova conexão rotaciona. A senha permanece constante. No Node, essa string inteira entra na URL de proxy que você passa para um agente de proxy.

Axios: use um agent, não a opção de proxy embutida

Aqui está a coisa mais importante deste post. O Axios tem uma opção proxy, e para destinos HTTPS com autenticação ela não é confiável, não abre um túnel CONNECT adequado e falha silenciosamente ou vaza seu IP real. A correção que o ecossistema adotou é passar ao Axios um httpsAgent construído a partir de https-proxy-agent, e definir proxy: false para que o Axios não tente lidar com o proxy por conta própria.

import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
const user = `${process.env.SHIFTER_USER}-country-us`;
const pass = process.env.SHIFTER_PASS;
const proxyUrl = `http://${user}:${pass}@p.shifter.io:443`;
const agent = new HttpsProxyAgent(proxyUrl);
const client = axios.create({
httpsAgent: agent,
proxy: false, // crítico: deixe o agent lidar com isso, não o Axios
timeout: 30000,
});
const res = await client.get('https://api.ipify.org');
console.log(res.data); // um IP residencial dos EUA

Duas coisas para internalizar. A string user inclui as flags de segmentação (-country-us), porque a geolocalização está ali. E proxy: false não é opcional, sem ele a lógica de proxy própria do Axios colide com o agent e você obtém exatamente o comportamento quebrado que estava tentando evitar. Essa única linha é o bug de proxy mais comum no Node.

Got: o agent vai no slot agent.https

O Got recebe o agent de proxy através da sua opção agent, indexada por protocolo. O mesmo https-proxy-agent por baixo dos panos, sem a dança do proxy: false porque o Got não tem tratamento de proxy embutido para atrapalhar.

import got from 'got';
import { HttpsProxyAgent } from 'https-proxy-agent';
const proxyUrl = `http://${user}:${pass}@p.shifter.io:443`;
const res = await got('https://api.ipify.org', {
agent: { https: new HttpsProxyAgent(proxyUrl) },
timeout: { request: 30000 },
});
console.log(res.body); // um IP residencial dos EUA

Uma observação sobre empacotamento: o Got é ESM puro desde a v12, então import got from 'got' precisa de um projeto ESM ("type": "module" no package.json) ou de um import() dinâmico. Se você estiver preso ao CommonJS, ou permaneça na v11 do Got ou mude para Axios/undici. Essa divisão ESM-versus-CommonJS é um obstáculo específico do Node, não uma questão de proxy, mas incomoda quem está configurando isso pela primeira vez.

fetch nativo: o ProxyAgent do undici como dispatcher

Node 18+ vem com um fetch global baseado em undici, e o undici tem seu próprio suporte a proxy que não usa https-proxy-agent de forma alguma. Você passa um ProxyAgent como o dispatcher da requisição:

import { ProxyAgent } from 'undici';
const dispatcher = new ProxyAgent(`http://${user}:${pass}@p.shifter.io:443`);
const res = await fetch('https://api.ipify.org', { dispatcher });
console.log(await res.text()); // um IP residencial dos EUA

Para rotear cada fetch do processo através do proxy, defina-o globalmente:

import { setGlobalDispatcher, ProxyAgent } from 'undici';
setGlobalDispatcher(new ProxyAgent(proxyUrl));

Se você está em uma versão moderna do Node e quer zero dependências de cliente HTTP, esse é o caminho mais limpo.

Armadilha 1: reutilize o agent, não construa um por requisição

Qualquer que seja o cliente escolhido, o agent de proxy é dono do pool de conexões. Construir um HttpsProxyAgent ou ProxyAgent novo a cada requisição descarta o keep-alive e paga um handshake TCP + TLS completo através do proxy toda vez, o overhead que o guia de latência existe para eliminar. Construa o agent uma vez para uma dada identidade e reutilize-o entre requisições. Crie o cliente Axios/Got (ou o dispatcher do undici) na inicialização e mantenha-o.

Armadilha 2: limite sua concorrência, o event loop não fará isso por você

O event loop do Node torna trivial disparar mil requisições de uma vez, e nada te impede disso. await Promise.all(urls.map(fetchOne)) em um array grande abrirá todas as conexões simultaneamente, o que esgota os sockets do seu lado e parece um ataque para o destino. Limite as requisições em andamento com um pequeno limitador de concorrência (p-limit é a escolha comum) ou uma fila simples:

import pLimit from 'p-limit';
const limit = pLimit(8); // no máximo 8 requisições em andamento
const results = await Promise.all(
urls.map(url => limit(() => client.get(url)))
);

Limite a concorrência por host de destino, não apenas globalmente, para que um site frágil não seja martelado enquanto um permissivo fica faminto. Mais paralelismo além da tolerância de um destino compra bloqueios, não throughput (how to avoid getting blocked). Ajuste o limite ao que cada host tolera.

Armadilha 3: erros assíncronos precisam de tratamento explícito

Uma falha de proxy ou conexão surge como uma promise rejeitada, e uma rejeição não tratada pode derrubar o processo ou, pior, descartar silenciosamente uma tarefa em um lote. Envolva cada requisição para que uma falha de transporte tente novamente com uma identidade nova em vez de derrubar a execução:

async function fetchWithRetry(client, url, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
return await client.get(url);
} catch (err) {
if (i === attempts - 1) throw err;
// transitório (ECONNRESET, timeout, proxy 5xx): recue e tente novamente
await new Promise(r => setTimeout(r, 500 * 2 ** i));
}
}
}

Distinga uma conexão quebrada que vale a pena tentar novamente de uma desaceleração deliberada; um timeout é uma tentativa quebrada, um 429 é o servidor pedindo espaço e deve fazer você recuar, não martelar.

Rotacionando geolocalização e sessões

Como a segmentação vive no username, uma identidade diferente é uma URL de proxy diferente, o que significa um agent diferente. O padrão eficiente é armazenar em cache um agent por identidade para manter o pool de conexões por sessão em vez de reconstruí-lo:

const agents = new Map();
function agentFor(country, sid) {
const key = `${country}:${sid ?? 'rotate'}`;
if (!agents.has(key)) {
const u = `${process.env.SHIFTER_USER}-country-${country}` +
(sid ? `-sid-${sid}-ttl-600` : '');
const url = `http://${u}:${process.env.SHIFTER_PASS}@p.shifter.io:443`;
agents.set(key, new HttpsProxyAgent(url));
}
return agents.get(key);
}
// por requisição:
await axios.get(targetUrl, { httpsAgent: agentFor('de', 'job-42'), proxy: false });

Dê a cada unidade lógica de trabalho seu próprio sid e rotacione entre unidades, não no meio de um fluxo (sticky vs rotating cobre essa distinção), e mapeie o trabalho para identidades da forma descrita no post sobre load balancing.

Verifique se você está realmente usando o proxy

Antes de fazer benchmark ou depurar qualquer outra coisa, confirme o IP de saída:

const res = await client.get('http://ip-api.com/json');
console.log(res.data); // espere um IP residencial no país segmentado

Ver seu próprio IP significa que o agent não está sendo aplicado (com Axios, quase sempre um proxy: false faltando). Uma travada significa que a saída local está bloqueada. Ambos são cobertos no guia de diagnóstico de timeout.

Perguntas frequentes

Por que a opção proxy do Axios não funciona com meu proxy HTTPS? A opção proxy embutida do Axios não faz o tunelamento de HTTPS com autenticação de forma confiável. Use um httpsAgent construído a partir de https-proxy-agent e defina proxy: false para que o Axios pare de tentar lidar com isso. Essa combinação é o caminho confiável e corrige o sintoma de “ele retorna meu IP real”.

Eu preciso do https-proxy-agent se uso o fetch nativo? Não. O fetch do Node 18+ é baseado em undici, que tem seu próprio ProxyAgent que você passa como dispatcher (ou define globalmente com setGlobalDispatcher). O https-proxy-agent é para Axios, Got e os módulos embutidos http/https.

Por que import got from 'got' lança um erro no meu projeto? O Got é ESM puro desde a v12, então precisa de um projeto ESM ("type": "module") ou de um import() dinâmico. Em CommonJS, permaneça na v11 do Got ou use Axios/undici. Isso é uma questão de sistema de módulos, não de proxy.

Como eu rotaciono IPs por requisição em Node.js? Varie o username do proxy, o que significa uma URL de proxy diferente e um agent diferente. Armazene em cache um agent por identidade em um Map para que cada sessão mantenha seu próprio pool de conexões, e escolha o agent por requisição. Omita o sid no username para rotacionar a cada nova conexão.

Axios, Got ou fetch para scraping? Os três funcionam. O fetch nativo + undici não tem dependências extras em versões modernas do Node; o Got tem retries e streams ergonômicos; o Axios é onipresente e familiar mas precisa da correção do proxy: false. Escolha com base na ergonomia e nas dependências existentes, a configuração de proxy e as armadilhas acima se aplicam aos três.

Conclusão

Node.js mais residential proxies é rápido de configurar assim que você conhece a regra não óbvia: com Axios, use um https-proxy-agent e defina proxy: false, nunca a opção de proxy embutida; com Got, coloque o agent em agent.https; com o fetch nativo, passe um ProxyAgent do undici como dispatcher. Depois reutilize o agent para que as conexões continuem quentes, limite sua concorrência porque o event loop não fará isso, trate erros assíncronos para que uma requisição ruim tente novamente em vez de travar, e varie o username do proxy para mudar geolocalização ou sessão.

Acerte isso e o Node lida com coleta simultânea tão bem quanto qualquer outra coisa. Aponte-o para o gateway residencial, e lembre-se de que a qualidade do pool decide com que frequência você precisa tentar novamente (IP reputation). A página de preços tem os planos por GB para testar isso com seus próprios destinos.

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