Se você está tentando usar proxies em Deno e Bun pela primeira vez, provavelmente já descobriu que fetch() simplesmente ignora suas configurações de proxy. Ao contrário do Node.js com undici ou bibliotecas como axios, os novos runtimes JavaScript tratam o proxy como uma configuração explícita do cliente HTTP, não uma convenção de variável de ambiente. Neste guia vamos resolver isso com código runnable — desde a configuração básica de Deno.createHttpClient até o parâmetro proxy do fetch no Bun, rotacionando sessões residenciais com ProxyHat em produção.
Por que o fetch() nativo ignora proxies em Deno e Bun
O fetch() global é definido pela spec do WHATWG Fetch, que não inclui proxy como parte da API padrão. No navegador, o proxy é responsabilidade do sistema operacional ou das configurações de rede. Em runtimes de servidor, cada implementação decide como — ou se — lida com proxies.
O Deno implementou suporte a proxy através de Deno.createHttpClient(), que aceita um objeto proxy com url e basicAuth. Esse cliente é então passado como { client } para fetch(). O Bun, por outro lado, adicionou suporte nativo ao proxy diretamente em fetch() através da opção proxy, como documentado na referência oficial do Bun. Ambas as abordagens funcionam, mas exigem configuração explícita — não há automagic.
Isso é na verdade uma vantagem: você tem controle granular sobre qual proxy usar em cada requisição, pode misturar proxies com diferentes geolocalizações no mesmo processo, e não depende de variáveis de ambiente que podem mudar entre deploys. O custo é que você precisa entender a API de cada runtime.
Configurando proxy no Deno com Deno.createHttpClient
No Deno, você cria um cliente HTTP dedicado com configurações de proxy e o passa para cada chamada de fetch(). Isso dá controle total: diferentes clientes podem usar proxies diferentes, com geolocalização e sessões distintas. O cliente também mantém um pool de conexões TCP reutilizável, o que melhora performance em alto volume.
// deno run --allow-net proxy_deno.ts
const PROXY_URL = "http://user-country-US:pass@gate.proxyhat.com:8080";
const client = Deno.createHttpClient({
proxy: {
url: PROXY_URL,
basicAuth: undefined, // já está embutido na URL
},
});
const res = await fetch("https://httpbin.org/ip", { client });
const body = await res.json();
console.log(body);
// { origin: "199.x.x.x" } ← IP do proxy, não o seu
client.close(); // libera recursos do pool de conexões
Note que o Deno exige a flag --allow-net para permitir conexões de saída. O método client.close() é importante em scripts longos para evitar vazamento de conexões. Em serviços de longa duração, crie o cliente uma vez e reutilize-o.
Autenticação básica separada da URL
Se preferir não colocar credenciais na URL — útil quando as credenciais vêm de um secret manager — você pode usar basicAuth:
const client = Deno.createHttpClient({
proxy: {
url: "http://gate.proxyhat.com:8080",
basicAuth: {
username: "user-country-DE",
password: Deno.env.get("PROXYHAT_PASS")!,
},
},
});
const res = await fetch("https://httpbin.org/ip", { client });
console.log(await res.json());
client.close();
Configurando proxy no Bun com fetch(url, { proxy })
O Bun tornou o uso de proxy incrivelmente simples: basta passar a string do proxy na opção proxy de fetch(). Não há cliente separado para gerenciar, não há método close() para chamar. É a abordagem mais ergonômica entre os três runtimes (Node, Deno, Bun).
// bun run proxy_bun.ts
const PROXY_URL = "http://user-country-US:pass@gate.proxyhat.com:8080";
const res = await fetch("https://httpbin.org/ip", {
proxy: PROXY_URL,
});
const body = await res.json();
console.log(body);
// { origin: "199.x.x.x" }
Essa abordagem é ideal para scripts rápidos e protótipos. Para produção com múltiplas requisições concorrentes, você pode querer reutilizar a string de proxy ou criar um wrapper que alterna entre sessões — veremos isso adiante.
Deno vs Bun: comparação de suporte a proxy
| Característica | Deno | Bun |
|---|---|---|
| API de proxy | Deno.createHttpClient({ proxy }) |
fetch(url, { proxy }) |
| Passagem para fetch | { client } |
{ proxy } |
| Reutilização de conexão | Sim (pool no HttpClient) | Não (nova conexão por fetch) |
| CA custom | caCerts: [pemString] |
Não suportado nativamente |
| SOCKS5 | Sim (via URL socks5://) |
Sim (via URL socks5://) |
Env vars HTTP_PROXY |
Sim (automático) | Não (requer proxy explícito) |
Geolocalização e sessões sticky no username
O ProxyHat codifica geo-targeting e sessões diretamente no nome de usuário. Isso significa que você não precisa de headers extras ou parâmetros de query — tudo vai na URL do proxy. Uma sessão sticky mantém o mesmo IP de saída durante o tempo que a sessão estiver ativa — ideal para fluxos de login multi-passo ou carrinhos de compras.
| Flag no username | Função | Exemplo |
|---|---|---|
country-XX |
Geo-targeting por país | user-country-US |
city-xxx |
Geo-targeting por cidade | user-country-DE-city-berlin |
session-xxx |
Sessão sticky (mesmo IP) | user-session-abc123 |
| Combinado | País + cidade + sessão | user-country-US-city-nyc-session-s1 |
Combine country com session para garantir que o IP está no país correto e permanece consistente. Consulte a lista de localizações para ver países e cidades disponíveis.
// Sessão sticky com geo-targeting — Deno
function makeClient(country: string, sessionId: string) {
const username = `user-country-${country}-session-${sessionId}`;
const proxyUrl = `http://${username}:pass@gate.proxyhat.com:8080`;
return Deno.createHttpClient({ proxy: { url: proxyUrl } });
}
// Cada cliente mantém um IP diferente
const usClient = makeClient("US", "sess-001");
const deClient = makeClient("DE", "sess-002");
const [usRes, deRes] = await Promise.all([
fetch("https://httpbin.org/ip", { client: usClient }),
fetch("https://httpbin.org/ip", { client: deClient }),
]);
console.log("US IP:", (await usRes.json()).origin);
console.log("DE IP:", (await deRes.json()).origin);
usClient.close();
deClient.close();
SOCKS5 na porta 1080
Para casos que exigem SOCKS5 — como tunneling de TCP puro ou compatibilidade com certos firewalls corporativos — o ProxyHat oferece SOCKS5 na porta 1080. O formato da URL muda para socks5://. Tanto Deno quanto Bun suportam SOCKS5 nativamente.
// Deno + SOCKS5
const socksClient = Deno.createHttpClient({
proxy: {
url: "socks5://user-country-JP:pass@gate.proxyhat.com:1080",
},
});
const res = await fetch("https://httpbin.org/ip", { client: socksClient });
console.log(await res.json());
socksClient.close();
// Bun + SOCKS5
const res = await fetch("https://httpbin.org/ip", {
proxy: "socks5://user-country-JP:pass@gate.proxyhat.com:1080",
});
console.log(await res.json());
Variáveis de ambiente HTTP_PROXY e HTTPS_PROXY
O Deno respeita HTTP_PROXY e HTTPS_PROXY automaticamente quando você usa fetch() sem um cliente customizado. O Bun, na versão atual, não lê essas variáveis automaticamente — você precisa passar proxy explicitamente ou usar um wrapper.
# Deno lê variáveis de ambiente automaticamente
export HTTP_PROXY="http://user-country-GB:pass@gate.proxyhat.com:8080"
deno run --allow-net --allow-env script.ts
Use variáveis de ambiente quando quiser configurar proxy globalmente sem modificar o código — útil em CI/CD ou containers. Prefira configuração por cliente (Deno.createHttpClient ou proxy no Bun) quando precisar de controle granular: diferentes sessões, países ou estratégias de rotação por request. Em pipelines de CI, as env vars são mais convenientes; em aplicações de scraping com múltiplas fontes, a configuração por cliente é essencial.
Por que proxies residenciais para alvos de alto bloqueio
Proxies residenciais usam IPs de dispositivos reais — casas, celulares, ISPs. Alvos com defesa anti-bot agressiva (SERPs, e-commerce, redes sociais) detectam e bloqueiam IPs de datacenter rapidamente, muitas vezes em segundos. Proxies residenciais têm taxa de sucesso significativamente maior nesses cenários porque o tráfego se mistura com usuários reais.
Para casos de uso como web scraping e SERP tracking, a combinação de IP residencial + geo-targeting + sessão sticky é o padrão ouro. Proxies datacenter funcionam bem para APIs públicas, testes de conectividade e acesso a conteúdo sem restrições — oferecendo menor latência (tipicamente < 50ms vs 200-500ms para residenciais) e custo reduzido.
Rotacionando sessões residenciais com Promise.all
Com ProxyHat, você cria um pool de sessões sticky e distribui requisições concorrentes entre elas. Cada sessão mantém um IP consistente, mas o pool inteiro usa múltiplos IPs — equilibrando consistência (para o alvo) com distribuição (para evitar rate limits).
// pool_rotacao.ts — funciona em Deno e Bun
const GATE = "gate.proxyhat.com:8080";
const USER = "user";
const PASS = "pass";
const SESSIONS = Array.from({ length: 10 }, (_, i) => `pool-${i}`);
function proxyUrl(country: string, session: string) {
return `http://${USER}-country-${country}-session-${session}:${PASS}@${GATE}`;
}
async function fetchWithTimeout(
url: string,
proxy: string,
timeoutMs = 10000,
): Promise<Response> {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
// Deno: passe { client }; Bun: passe { proxy }
// Aqui usamos o padrão Bun para simplicidade
const res = await fetch(url, {
proxy,
signal: controller.signal,
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res;
} finally {
clearTimeout(timer);
}
}
// Distribui 20 requisições entre 10 sessões
const targets = Array.from({ length: 20 }, (_, i) =>
`https://httpbin.org/anything?req=${i}`
);
const results = await Promise.allSettled(
targets.map((url, i) => {
const session = SESSIONS[i % SESSIONS.length];
const proxy = proxyUrl("US", session);
return fetchWithTimeout(url, proxy);
}),
);
const ok = results.filter((r) => r.status === "fulfilled").length;
const failed = results.filter((r) => r.status === "rejected").length;
console.log(`Sucessos: ${ok}, Falhas: ${failed}`);
Com 10 sessões sticky, cada sessão faz ~2 requisições — o que mantém o IP consistente dentro de cada sessão, mas distribui a carga entre 10 IPs diferentes. O AbortController com timeout de 10 segundos evita que uma requisição travada segure o pool inteiro. Promise.allSettled garante que uma falha não aborte as demais.
Dicas de produção: retries, backoff, CA custom e logging
Retry com backoff exponencial
Redes instáveis, timeouts de proxy e rate limits temporários são comuns. Um retry com backoff exponencial resolve a maioria dos problemas transitórios sem intervenção manual.
async function fetchWithRetry(
url: string,
proxy: string,
maxRetries = 3,
): Promise<Response> {
let lastError: Error | null = null;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const res = await fetchWithTimeout(url, proxy);
if (res.status === 429 || res.status >= 500) {
throw new Error(`HTTP ${res.status} — retry`);
}
return res;
} catch (err) {
lastError = err as Error;
if (attempt < maxRetries) {
const backoff = Math.min(1000 * 2 ** attempt, 8000);
console.warn(
`Tentativa ${attempt + 1} falhou: ${lastError.message}. ` +
`Retry em ${backoff}ms`
);
await new Promise((r) => setTimeout(r, backoff));
}
}
}
throw lastError;
}
O backoff começa em 1000ms e dobra a cada tentativa, com teto de 8000ms. Isso evita sobrecarregar o proxy ou o alvo enquanto dá tempo para a condição de erro se resolver. Para 429 (Too Many Requests), considere também respeitar o header Retry-After se presente.
CA custom no Deno
Se você está atrás de um proxy corporativo com inspeção TLS, pode precisar adicionar CA certificates customizados. O Deno suporta isso nativamente via caCerts:
const caCert = await Deno.readTextFile("./corp-ca.pem");
const client = Deno.createHttpClient({
proxy: {
url: "http://user-country-US:pass@gate.proxyhat.com:8080",
},
caCerts: [caCert], // array de strings PEM
});
const res = await fetch("https://httpbin.org/ip", { client });
console.log(await res.json());
client.close();
Reutilização de conexões e logging
No Deno, reutilizar o mesmo HttpClient entre requisições com o mesmo proxy melhora performance — o cliente mantém o pool de conexões TCP. No Bun, cada fetch() com proxy cria uma nova conexão proxy, então não há reutilização no mesmo nível. Para alto volume no Bun, considere agrupar requisições por sessão e usar Bun.spawn para paralelismo controlado.
Para logging, registre o IP de saída (via httpbin.org/ip ou o header x-forwarded-for retornado), o tempo de resposta e o status code. Isso ajuda a identificar sessões problemáticas antes que elas afetem a taxa de sucesso global.
ProxyHat Node SDK: compatível com Deno e Bun
Se você prefere uma abstração em vez de URLs manuais, o SDK do ProxyHat para Node.js funciona sob Deno e Bun via compatibilidade com npm. Veja a documentação oficial para detalhes de instalação.
// Instalação: npm install @proxyhat/node-sdk
// ou: deno add npm:@proxyhat/node-sdk
import { ProxyHat } from "@proxyhat/node-sdk";
const ph = new ProxyHat({
username: "user",
password: "pass",
defaultCountry: "US",
});
// O SDK gera a URL do proxy automaticamente
const proxyUrl = ph.getProxyUrl({
country: "DE",
city: "berlin",
session: "my-session",
});
// → http://user-country-DE-city-berlin-session-my-session:pass@gate.proxyhat.com:8080
// Use com fetch nativo
const res = await fetch("https://httpbin.org/ip", {
proxy: proxyUrl, // Bun
});
// Ou com Deno
const client = Deno.createHttpClient({ proxy: { url: proxyUrl } });
const res2 = await fetch("https://httpbin.org/ip", { client });
client.close();
O SDK abstrai a construção de URLs, mas o resultado é o mesmo: uma string http://user-...:pass@gate.proxyhat.com:8080. Use o SDK quando quiser validação de tipos e helpers; use URLs manuais quando precisar de controle máximo e zero dependências.
Scraping ético: dados públicos, CFAA e GDPR
Antes de coletar dados, considere três pontos críticos:
- Use APIs oficiais primeiro. Muitos sites oferecem APIs públicas ou planos pagos que são mais confiáveis e legalmente seguros que scraping. Se existe uma API, use-a.
- Dados públicos apenas. Nos EUA, o Computer Fraud and Abuse Act (CFAA) pode se aplicar a acessos não autorizados. Na UE, o GDPR rege o processamento de dados pessoais — mesmo dados publicamente disponíveis na web podem estar sujeitos a restrições de processamento.
- Respeite
robots.txte os Termos de Serviço. Proxy residencial não torna o scraping legal; apenas reduz bloqueios técnicos. A ética e a legalidade vêm antes da infraestrutura.
Mantenha taxas de requisição razoáveis (1-2 req/s por sessão) e evite sobrecarregar o servidor alvo. Se você está coletando dados para pesquisa ou análise de mercado, considere usar dados já agregados por provedores especializados.
Principais conclusões
- Deno: use
Deno.createHttpClient({ proxy: { url } })e passe como{ client }emfetch(). Reutilize o cliente para connection pooling.- Bun: use
fetch(url, { proxy: "http://..." })— uma linha só, sem cliente para gerenciar.- Geo + sessão: codifique no username (
user-country-US-session-abc123) — sem headers extras.- SOCKS5: porta
1080comsocks5://em ambos os runtimes.- Produção: retries com backoff exponencial (1000ms base, 8000ms teto),
AbortControllerpara timeout de 10s,Promise.allSettledpara tolerância a falhas.- Ética: dados públicos, APIs primeiro, respeite
robots.txte ToS.
Pronto para começar? Confira os planos e preços do ProxyHat ou explore todas as localizações disponíveis para seu próximo projeto em Deno ou Bun.






