Pourquoi fetch() ignore les proxies dans Deno et Bun
Si vous avez déjà essayé de router une requête fetch() à travers un proxy dans Deno ou Bun, vous avez probablement remarqué que les variables d'environnement HTTP_PROXY et HTTPS_PROXY ne sont pas automatiquement respectées par fetch(). Ce comportement n'est pas un bug : la spécification Fetch du WHATWG ne mentionne tout simplement pas les proxies. Le standard fetch() décrit comment faire des requêtes HTTP, mais délègue la configuration réseau à l'environnement hôte. Résultat : chaque runtime JavaScript doit implémenter sa propre solution.
Utiliser des proxies dans Deno et Bun est donc une question légitime pour tout développeur qui scrape, automatise ou collecte des données. Deno expose Deno.createHttpClient({ proxy: { url, basicAuth } }), qu'on passe ensuite à fetch() via l'option { client }. Bun, lui, a ajouté une option proxy directement dans fetch(url, { proxy }). Les deux approches sont différentes mais aboutissent au même résultat : vos requêtes sortent via l'IP du proxy.
Le tableau ci-dessous résume les différences clés entre les deux runtimes :
| Critère | Deno | Bun |
|---|---|---|
| API proxy native | Deno.createHttpClient({ proxy }) + fetch(url, { client }) | fetch(url, { proxy }) |
| Support SOCKS5 | Oui (via url: "socks5://...") | Oui |
Variables d'env HTTP_PROXY | Non automatique avec fetch | Non automatique avec fetch |
| CA personnalisée | caCerts dans createHttpClient | Variables d'environnement système |
| Version minimale recommandée | Deno 1.28+ | Bun 1.0.13+ |
Contexte technique : pourquoi ce problème existe
Node.js a historiquement reposé sur http.Agent et des bibliothèques comme proxy-agents pour gérer les proxies. Deno et Bun, conçus à l'ère de fetch() standardisé, ont pris une approche différente. Au lieu de patcher le DNS global ou d'intercepter les sockets, ils exposent une configuration de proxy au niveau du client HTTP. C'est plus propre, mais cela signifie que vous devez explicitement configurer chaque appel.
Pour le scraping et l'automatisation, c'est en fait un avantage : vous pouvez utiliser différents proxies pour différentes requêtes dans le même processus, ce qui est idéal pour la rotation d'IP et les sessions sticky géo-ciblées.
Mise en œuvre dans Deno avec Deno.createHttpClient
Deno fournit Deno.createHttpClient() qui retourne un objet client que vous passez à fetch(). Voici un exemple minimal avec un proxy ProxyHat :
// deno run --allow-net app.ts
const proxyUrl = "http://user-country-US:pass@gate.proxyhat.com:8080";
const client = Deno.createHttpClient({
proxy: {
url: proxyUrl,
},
});
try {
const res = await fetch("https://httpbin.org/ip", { client });
const data = await res.json();
console.log("IP de sortie :", data.origin);
} catch (err) {
console.error("Erreur proxy :", err.message);
} finally {
client.close();
}
Notez l'appel explicite à client.close(). Deno ne ferme pas automatiquement le client HTTP, ce qui peut causer des fuites de connexions dans un script long-running. En production, gardez une référence au client et fermez-le proprement.
Authentification Basic explicite
Si votre mot de passe contient des caractères spéciaux, encodez-les dans l'URL ou utilisez basicAuth :
const client = Deno.createHttpClient({
proxy: {
url: "http://gate.proxyhat.com:8080",
basicAuth: {
username: "user-country-DE-city-berlin",
password: "p@ss:w0rd!",
},
},
});
const res = await fetch("https://httpbin.org/headers", { client });
console.log(await res.text());
client.close();
Mise en œuvre dans Bun avec fetch + proxy
Bun a la syntaxe la plus simple : passez l'URL du proxy directement dans l'option proxy de fetch().
// bun run app.ts
const proxyUrl = "http://user-country-FR:pass@gate.proxyhat.com:8080";
try {
const res = await fetch("https://httpbin.org/ip", {
proxy: proxyUrl,
});
const data = await res.json();
console.log("IP de sortie :", data.origin);
} catch (err) {
console.error("Erreur proxy :", err.message);
}
Aucune ressource explicite à fermer. Bun gère le pool de connexions en arrière-plan. C'est plus concis, mais cela signifie aussi moins de contrôle granulaire sur le cycle de vie du client.
Geo-targeting et sessions sticky dans le nom d'utilisateur
ProxyHat encode le geo-targeting et les sessions sticky directement dans le nom d'utilisateur. Le format est user-country-XX-session-YYY. Cela vous permet de cibler un pays spécifique et de maintenir la même IP de sortie pour toutes les requêtes d'une même session — essentiel pour les sites qui invalident les paniers ou les sessions de connexion quand l'IP change.
// Deno — session sticky géo-ciblée
function makeClient(country: string, session: string) {
const username = `user-country-${country}-session-${session}`;
const proxyUrl = `http://${username}:pass@gate.proxyhat.com:8080`;
return Deno.createHttpClient({ proxy: { url: proxyUrl } });
}
// Session sticky pour les États-Unis
const usClient = makeClient("US", "order-abc123");
const r1 = await fetch("https://httpbin.org/ip", { client: usClient });
console.log("Req 1:", (await r1.json()).origin);
const r2 = await fetch("https://httpbin.org/ip", { client: usClient });
console.log("Req 2:", (await r2.json()).origin);
// Les deux requêtes sortent par la même IP
usClient.close();
SOCKS5 sur le port 1080
Si vous préférez SOCKS5 (utile pour le tunneling TCP ou les environnements où HTTP CONNECT est bloqué), utilisez le port 1080 :
// Bun — SOCKS5
const socksProxy = "socks5://user-country-JP:pass@gate.proxyhat.com:1080";
const res = await fetch("https://httpbin.org/ip", {
proxy: socksProxy,
});
console.log(await res.json());
Variables d'environnement : HTTP_PROXY et HTTPS_PROXY
Les deux runtimes ignorent HTTP_PROXY/HTTPS_PROXY pour fetch() par défaut. Vous pouvez cependant les lire manuellement et les injecter dans votre configuration de proxy. C'est utile pour la configuration via environnement (12-factor app) :
// Deno — lire HTTP_PROXY depuis l'environnement
const proxyEnv = Deno.env.get("HTTPS_PROXY") ?? Deno.env.get("HTTP_PROXY");
if (!proxyEnv) {
console.error("Aucun proxy configuré. Définissez HTTPS_PROXY.");
Deno.exit(1);
}
const client = Deno.createHttpClient({
proxy: { url: proxyEnv },
});
const res = await fetch("https://httpbin.org/ip", { client });
console.log(await res.json());
client.close();
Préférez la configuration par client (createHttpClient ou fetch({ proxy })) quand vous avez besoin de rotation d'IP ou de proxies différents par requête. Les variables d'environnement sont bien pour un proxy unique global, par exemple en CI/CD ou en conteneur.
Rotation concurrente de sessions sticky avec Promise.all
Pour les cibles à forte protection anti-bot, les proxies résidentiels sont indispensables. Les IPs datacenter sont souvent bloquées dès la première requête. Voici un exemple qui lance 10 requêtes concurrentes, chacune avec une session sticky différente, un timeout via AbortController et une rotation de pays :
// Bun — rotation concurrente avec timeout
const COUNTRIES = ["US", "DE", "FR", "GB", "JP", "CA", "AU", "ES", "IT", "NL"];
const TIMEOUT_MS = 10000;
async function fetchWithProxy(url: string, country: string, sessionId: string) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
const proxyUrl = `http://user-country-${country}-session-${sessionId}:pass@gate.proxyhat.com:8080`;
try {
const res = await fetch(url, {
proxy: proxyUrl,
signal: controller.signal,
});
return await res.json();
} catch (err) {
if (err.name === "AbortError") {
console.error(`Timeout pour ${country}/${sessionId}`);
return null;
}
throw err;
} finally {
clearTimeout(timeout);
}
}
const tasks = COUNTRIES.map((country, i) =>
fetchWithProxy("https://httpbin.org/ip", country, `pool-${i}-${Date.now()}`)
);
const results = await Promise.all(tasks);
results.forEach((r, i) => {
if (r) console.log(`${COUNTRIES[i]}: ${r.origin}`);
});
Avec 10 sessions sticky concurrentes, vous obtenez 10 IPs résidentielles différentes réparties sur 10 pays. Le AbortController garantit qu'une requête lente ne bloque pas tout le pool. En pratique, visez un timeout de 10 à 15 secondes et un taux d'erreur acceptable de 5 à 10 %.
Production : retries, backoff, CA personnalisée et réutilisation de connexions
Retry avec backoff exponentiel
// Deno — retry avec backoff exponentiel
async function fetchWithRetry(
url: string,
client: Deno.HttpClient,
maxRetries = 3,
): Promise<Response> {
let lastErr: Error | null = null;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const res = await fetch(url, { client });
if (res.status === 429 || res.status >= 500) {
throw new Error(`HTTP ${res.status}`);
}
return res;
} catch (err) {
lastErr = err as Error;
if (attempt < maxRetries) {
const delay = Math.min(1000 * 2 ** attempt, 8000); // 1s, 2s, 4s, max 8s
console.warn(`Tentative ${attempt + 1} échouée (${err.message}), retry dans ${delay}ms`);
await new Promise((r) => setTimeout(r, delay));
}
}
}
throw lastErr;
}
const client = Deno.createHttpClient({
proxy: { url: "http://user-country-US:pass@gate.proxyhat.com:8080" },
});
const res = await fetchWithRetry("https://httpbin.org/status/200", client);
console.log("Statut final :", res.status);
client.close();
CA personnalisée via caCerts
Deno permet de spécifier des certificats d'autorité personnalisés avec caCerts, utile en environnement d'entreprise avec un proxy TLS interceptant :
const caCert = await Deno.readTextFile("./custom-ca.pem");
const client = Deno.createHttpClient({
proxy: {
url: "http://user-country-US:pass@gate.proxyhat.com:8080",
},
caCerts: [caCert],
});
const res = await fetch("https://httpbin.org/ip", { client });
console.log(await res.json());
client.close();
Réutilisation de connexions
Dans Deno, réutilisez le même HttpClient pour plusieurs requêtes vers le même hôte. Deno met en pool les connexions TCP/TLS sous le capot, ce qui réduit la latence de 100 à 200 ms par requête après la première. Évitez de créer un nouveau client pour chaque requête — c'est l'erreur la plus courante et elle peut diviser votre débit par 3.
ProxyHat Node SDK : compatible Deno et Bun
Le SDK Node officiel de ProxyHat fonctionne aussi sous Bun (compatibilité npm native). Pour Deno, vous pouvez l'importer via npm: specifier. Voici une comparaison côte à côte :
// Bun — ProxyHat SDK
import { ProxyHat } from "proxyhat-sdk";
const ph = new ProxyHat({
username: "user-country-US-session-abc123",
password: "pass",
});
const ip = await ph.fetch("https://httpbin.org/ip");
console.log(await ip.json());
// Deno — ProxyHat SDK via npm specifier
import { ProxyHat } from "npm:proxyhat-sdk";
const ph = new ProxyHat({
username: "user-country-DE-session-xyz789",
password: "pass",
});
const res = await ph.fetch("https://httpbin.org/ip");
console.log(await res.json());
Le SDK gère automatiquement la rotation, les retries et le formatage du nom d'utilisateur. Consultez la documentation officielle ProxyHat pour les options avancées. Pour les tarifs et les plans, voir notre page de tarification.
Bonnes pratiques et éthique du scraping
Avant de scraper, demandez-vous si une API officielle existe. Beaucoup de plateformes (Google, Amazon, GitHub) offrent des API documentées qui sont plus fiables et légales que le scraping. Le scraping de données publiques est généralement acceptable, mais le contournement de mesures anti-bot peut violer les conditions d'utilisation voire des lois comme le Computer Fraud and Abuse Act (CFAA) aux États-Unis ou le RGPD dans l'UE pour les données personnelles.
- Respectez
robots.txtet les conditions d'utilisation. - Limitez votre taux de requêtes (max 1 à 2 req/s par domaine pour les sites sensibles).
- Ne collectez que des données publiques, jamais d'informations personnelles sans base légale.
- Identifiez votre user-agent de manière honnête quand c'est approprié.
Pour des cas d'usage comme le web scraping ou le suivi SERP, les proxies résidentiels de ProxyHat offrent un taux de succès de plus de 99 % sur la plupart des cibles. Consultez nos emplacements disponibles pour le geo-targeting.
Points clés à retenir
- fetch() n'gère pas les proxies nativement — Deno utilise
Deno.createHttpClient({ proxy }), Bun utilisefetch(url, { proxy }).- Geo-targeting et sessions sticky s'encodent dans le nom d'utilisateur :
user-country-US-session-abc123.- SOCKS5 est disponible sur le port
1080; HTTP sur8080.- Réutilisez les clients HTTP pour bénéficier du pooling de connexions (gain de 100 à 200 ms par requête).
- Rotation concurrente avec
Promise.all+AbortControllerpour des pools de sessions sticky.- Éthique d'abord : préférez les API officielles, respectez
robots.txtet les lois applicables.
FAQ
Qu'est-ce que l'utilisation de proxies dans Deno et Bun ?
Configurer des proxies dans Deno et Bun signifie router les requêtes HTTP de fetch() à travers une IP intermédiaire. Deno utilise Deno.createHttpClient({ proxy: { url, basicAuth } }) passé à fetch() via { client }, tandis que Bun accepte fetch(url, { proxy }) directement. Les deux runtimes ignorent les variables d'environnement HTTP_PROXY par défaut, ce qui rend la configuration explicite nécessaire.
Pourquoi utiliser des proxies dans Deno et Bun est-il important ?
Sans proxy, toutes vos requêtes sortent avec l'IP de votre serveur, ce qui est rapidement bloqué par les systèmes anti-bot. Les proxies résidentiels permettent de répartir les requêtes sur des centaines d'IPs, d'accéder à du contenu géo-restreint et de maintenir des sessions persistantes. Pour le scraping sérieux, c'est indispensable — un taux de succès de 99 % contre 30 à 50 % sans proxy sur les cibles protégées.
Quel type de proxy fonctionne le mieux pour Deno et Bun ?
Les proxies résidentiels sont recommandés pour les cibles à forte protection anti-bot, car ils utilisent des IPs d'appareils réels et sont rarement bloqués. Les proxies datacenter sont plus rapides et moins chers, adaptés aux tâches non sensibles. Les proxies mobiles offrent le plus haut niveau de confiance mais à un coût plus élevé. ProxyHat propose les trois types sur gate.proxyhat.com:8080 (HTTP) et :1080 (SOCKS5).
Comment éviter les blocages avec des proxies dans Deno et Bun ?
Utilisez des sessions sticky pour maintenir une IP cohérente pendant une session, faites tourner les pays et les sessions entre les lots de requêtes, implémentez un retry avec backoff exponentiel, et ajoutez un timeout via AbortController. Limitez le taux de requêtes à 1-2 par seconde par domaine, respectez robots.txt, et préférez les proxies résidentiels qui offrent un taux de succès supérieur à 99 %.
Peut-on utiliser le SDK Node de ProxyHat dans Deno et Bun ?
Oui. Bun est compatible npm nativement, donc import { ProxyHat } from "proxyhat-sdk" fonctionne directement. Pour Deno, utilisez le specifier npm:proxyhat-sdk. Le SDK gère automatiquement le formatage du nom d'utilisateur, la rotation et les retries, ce qui simplifie le code par rapport à une configuration manuelle de Deno.createHttpClient ou fetch({ proxy }).






