---
title: "Como usar proxy no Node.js: Axios e node-fetch"
description: "Como usar proxy no Node.js com Axios, node-fetch e fetch nativo, com código que funciona. Inclui autenticação, SOCKS5, rotação e tratamento de erros."
url: https://proxynet.io/pt-br/blog/nodejs-proxy
date: 2026-09-13
author: "Acar Diveroli"
category: "Tutoriais, Integração"
lang: pt-BR
---

# Como usar proxy no Node.js: Axios e node-fetch

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.

> **Nota: Resposta rápida**
>
> No fetch nativo, use juntos o `fetch` e o `ProxyAgent` do undici, ou inicie o Node.js com `NODE_USE_ENV_PROXY=1` e a variável de ambiente `HTTPS_PROXY`. No Axios, para endereços HTTPS, passe como `httpsAgent` um agent criado com `https-proxy-agent` e defina `proxy: false`. No node-fetch, o mesmo agent vai na opção `agent`. Para SOCKS5, use o pacote `socks-proxy-agent`.

## 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.

1. **Variável de ambiente.** As variáveis `HTTP_PROXY`, `HTTPS_PROXY` e `NO_PROXY`. Elas fazem um script inteiro passar pelo proxy sem mudar o código, mas nem toda biblioteca as lê.
2. **Agent.** Os módulos clássicos `http` e `https` do Node.js abrem conexões por um objeto `Agent`. 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 o `HttpsProxyAgent`), as requisições passam pelo proxy.
3. **Dispatcher.** O `fetch` nativo do Node.js não usa o módulo clássico `http`; usa um cliente chamado undici. No undici, o objeto que gerencia as conexões se chama `dispatcher`, e para proxy você passa um `ProxyAgent`.

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?](/pt-br/blog/what-is-a-proxy-server). Traduzir comandos cURL para fetch e Axios (cabeçalhos, corpo, dados de formulário) é o assunto do nosso artigo [cURL em JavaScript](/pt-br/blog/curl-in-javascript); este artigo foca só na parte do proxy.

## Como usar proxy no fetch nativo?

### ProxyAgent do undici

Instale o pacote undici:

```bash
npm install undici
```

Depois importe `fetch` e `ProxyAgent` **do mesmo pacote**:

```javascript
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:

```javascript
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 dispatcher
```

### A 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](https://nodejs.org/learn/http/enterprise-network-configuration) 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:

```bash
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://user:pass@pr.proxynet.io:8000" node app.mjs
```

No Windows PowerShell:

```powershell
$env:NODE_USE_ENV_PROXY = "1"
$env:HTTPS_PROXY = "http://user:pass@pr.proxynet.io:8000"
node app.mjs
```

Esse 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:

```javascript
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](https://axios-http.com/docs/req_config) do Axios funciona com endereços `http://` sem criptografia:

```javascript
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:

```bash
npm install axios https-proxy-agent
```

```javascript
import 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`:

```bash
npm install node-fetch https-proxy-agent
```

```javascript
import 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:

```bash
npm install socks-proxy-agent
```

```javascript
import 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](/pt-br/blog/webrtc-dns-leak). Antes de escolher entre SOCKS5 e proxy HTTP, veja [SOCKS ou HTTP proxy](/pt-br/blog/socks-vs-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](/pt-br/blog/proxy-authentication-methods).

## 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.

```javascript
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:

```javascript
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.allSettled`** evita que uma requisição com falha em um lote pare as outras. Com `Promise.all`, um único `503` faria perder os resultados do lote inteiro. No exemplo, o endereço `/status/503` foi marcado como `rejected` depois 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](https://proxynet.io/pt-br/rotating-proxy) 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](https://proxynet.io/pt-br/sticky-proxy).

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](/pt-br/blog/http-status-codes-web-scraping). Como o valor de concorrência afeta de fato a velocidade está em [Concorrência e paralelismo](/pt-br/blog/concurrency-vs-parallelism).

## 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:

```javascript
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 `agent` para o fetch nativo.** A opção é ignorada em silêncio. A chave certa no fetch é `dispatcher`.
- **Passar o `ProxyAgent` do undici do npm para o fetch global.** Quando as versões não batem, você recebe um erro `fetch failed`; importe também o `fetch` do undici.
- **Definir `HTTPS_PROXY` e supor que o fetch nativo vai ler.** Sem `NODE_USE_ENV_PROXY` ou `--use-env-proxy`, ele não lê.
- **Não escrever `proxy: false` com 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://`.** Use `socks5h://` 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.timeout` no undici e `timeout` no 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](/pt-br/blog/windows-chrome-proxy-settings).

## 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](/pt-br/blog/puppeteer-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?](/pt-br/blog/web-scraping-javascript-vs-python), e para bibliotecas Python veja [HTTPX, Requests ou AIOHTTP](/pt-br/blog/httpx-vs-requests-vs-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](/pt-br/data-scraping).
