Использование прокси в Deno и Bun: руководство для разработчиков

Полное руководство по настройке прокси в Deno и Bun: Deno.createHttpClient, fetch с опцией proxy, geo-targeting, sticky-сессии, SOCKS5 и production-советы с runnable-примерами кода.

Using Proxies in Deno and Bun: A Code-First Guide
В этой статье

Использование прокси в Deno и Bun — зачем и как

Если вы когда-либо пытались выполнить HTTP-запрос через прокси в современном JavaScript-рантайме, вы наверняка столкнулись с сюрпризом: fetch() игнорирует прокси по умолчанию. Ни Deno, ни Bun не подхватывают настройки прокси автоматически — в отличие от Node.js с его экосистемой https-proxy-agent. Использование прокси в Deno и Bun требует явной конфигурации, но зато даёт точный контроль над каждым запросом.

Это руководство показывает, как настроить Deno прокси через Deno.createHttpClient и Bun fetch прокси через встроенную опцию proxy, с runnable-примерами, geo-targeting, sticky-сессиями и production-паттернами. Все примеры используют шлюз ProxyHat gate.proxyhat.com:8080 (HTTP) и gate.proxyhat.com:1080 (SOCKS5).

Почему fetch() игнорирует прокси по умолчанию

Стандарт fetch(), определённый MDN Web Docs, не включает параметр прокси в сигнатуру запроса. Это намеренное решение: fetch спроектирован для браузерного контекста, где прокси управляются на уровне ОС или браузера. Серверные рантаймы наследуют этот интерфейс, но добавляют собственные расширения.

Deno решает это через Deno.createHttpClient() — фабрику, возвращающую объект клиента, который передаётся в fetch(url, { client }). Bun идёт другим путём: добавляет опцию proxy прямо в сигнатуру fetch(url, { proxy }). Оба подхода работают, но имеют разные trade-off'ы.

АспектDenoBun
Метод настройки проксиDeno.createHttpClient({ proxy: { url, basicAuth } })fetch(url, { proxy: 'http://user:pass@host:port' })
ГранулярностьPer-client (переиспользуемый объект)Per-request (строка в опциях)
SOCKS5 поддержкаЧерез URL-схему socks5://Через URL-схему socks5://
CA-сертификатыcaCerts в createHttpClientПеременные окружения
Connection reuseАвтоматически через клиентАвтоматически через пул

Настройка Deno прокси через Deno.createHttpClient

Согласно официальной документации Deno, Deno.createHttpClient принимает объект с полем proxy, содержащим url и опционально basicAuth. Полученный клиент передаётся в fetch через опцию client.

// deno run --allow-net proxy_deno.ts

const proxyUrl = "http://gate.proxyhat.com:8080";
const username = "user-country-US";
const password = "your-password";

// Создаём HTTP-клиент с прокси
const client = Deno.createHttpClient({
  proxy: {
    url: proxyUrl,
    basicAuth: {
      username,
      password,
    },
  },
});

// Используем клиент в fetch
const response = await fetch("https://httpbin.org/ip", {
  client,
});

const data = await response.json();
console.log("IP через прокси:", data.origin);

Ключевое преимущество подхода Deno — переиспользование клиента. Создав client один раз, вы можете использовать его в десятках запросов, и Deno будет поддерживать keep-alive соединения. Это снижает latency на ~200ms при повторных запросах.

Переменные окружения в Deno

Deno также поддерживает HTTP_PROXY и HTTPS_PROXY через флаг --allow-env, но per-client конфигурация предпочтительна, когда вам нужны разные прокси для разных задач:

// Разные клиенты для разных geo-таргетов
const usClient = Deno.createHttpClient({
  proxy: {
    url: "http://gate.proxyhat.com:8080",
    basicAuth: { username: "user-country-US", password: "pass" },
  },
});

const deClient = Deno.createHttpClient({
  proxy: {
    url: "http://gate.proxyhat.com:8080",
    basicAuth: { username: "user-country-DE", password: "pass" },
  },
});

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);

Настройка Bun fetch прокси

Bun, согласно документации Bun, добавляет опцию proxy прямо в fetch. Это проще в использовании, но менее гибко — нет переиспользуемого клиента с общим пулом соединений.

// bun run proxy_bun.ts

const proxyString = "http://user-country-US:your-password@gate.proxyhat.com:8080";

const response = await fetch("https://httpbin.org/ip", {
  proxy: proxyString,
});

const data = await response.json();
console.log("IP через прокси:", data.origin);

Для прокси fetch JavaScript в Bun это всё, что нужно — одна строка. Но если вы делаете много запросов, создавайте вспомогательную функцию:

// bun run proxy_bun_helper.ts

function createProxyUrl(opts: {
  country?: string;
  city?: string;
  session?: string;
  password: string;
}): string {
  let username = "user";
  if (opts.country) username += `-country-${opts.country}`;
  if (opts.city) username += `-city-${opts.city}`;
  if (opts.session) username += `-session-${opts.session}`;
  return `http://${username}:${opts.password}@gate.proxyhat.com:8080`;
}

const password = "your-password";

// Sticky-сессия для последовательных запросов
const stickyProxy = createProxyUrl({
  country: "US",
  session: "order-check-001",
  password,
});

const res1 = await fetch("https://httpbin.org/ip", { proxy: stickyProxy });
const res2 = await fetch("https://httpbin.org/headers", { proxy: stickyProxy });

console.log("IP 1:", (await res1.json()).origin);
console.log("IP 2:", (await res2.json()).origin); // Тот же IP

Geo-targeting и sticky-сессии в username

ProxyHat кодирует geo-таргетинг и sticky-сессии прямо в username. Формат: user-country-{CC}-city-{city}-session-{id}. Это работает одинаково для HTTP (порт 8080) и SOCKS5 (порт 1080).

// deno run --allow-net geo_sessions.ts (или bun run geo_sessions.ts)

const PASSWORD = "your-password";

// US, Нью-Йорк, sticky-сессия
const usNyProxy = "http://user-country-US-city-newyork-session-abc123:" + PASSWORD + "@gate.proxyhat.com:8080";

// Германия, Берлин, rotating (без session)
const deProxy = "http://user-country-DE-city-berlin:" + PASSWORD + "@gate.proxyhat.com:8080";

// Япония, Токио, sticky-сессия для скрапинга SERP
const jpProxy = "http://user-country-JP-city-tokyo-session-serp-42:" + PASSWORD + "@gate.proxyhat.com:8080";

console.log("US NY:", usNyProxy);
console.log("DE Berlin:", deProxy);
console.log("JP Tokyo:", jpProxy);

Sticky-сессии сохраняют один IP на протяжении всей сессии — это критично для сайтов с stateful-авторизацией (корзины, многошаговые формы). Время жизни sticky-сессии зависит от тарифа; подробности на странице тарифов ProxyHat.

SOCKS5 на порту 1080

Для задач, где нужен TCP-уровень (например, обход HTTP-фильтрации), ProxyHat поддерживает SOCKS5 на порту 1080. В Deno и Bun SOCKS5 указывается через URL-схему socks5://.

// Deno SOCKS5
const socksClient = Deno.createHttpClient({
  proxy: {
    url: "socks5://gate.proxyhat.com:1080",
    basicAuth: {
      username: "user-country-GB-session-socks-1",
      password: "your-password",
    },
  },
});

const res = await fetch("https://httpbin.org/ip", { client: socksClient });
console.log("SOCKS5 IP:", (await res.json()).origin);

// Bun SOCKS5
const socksProxy = "socks5://user-country-GB-session-socks-1:your-password@gate.proxyhat.com:1080";
const bunRes = await fetch("https://httpbin.org/ip", { proxy: socksProxy });
console.log("Bun SOCKS5 IP:", (await bunRes.json()).origin);

Переменные окружения HTTP_PROXY и HTTPS_PROXY

Оба рантайма поддерживают переменные окружения HTTP_PROXY и HTTPS_PROXY. Это удобно для quick-and-dirty скриптов, но per-client конфигурация предпочтительна в production:

  • Переменные окружения глобальны — все запросы идут через один прокси, даже те, которым это не нужно.
  • Нельзя задать разные geo-таргеты для разных частей приложения.
  • Сложно отлаживать: прокси «невидим» в коде.
  • Невозможно управлять sticky-сессиями — каждый запрос получает новый IP.
# Установка через переменные окружения (оба рантайма)
export HTTP_PROXY="http://user-country-US:your-password@gate.proxyhat.com:8080"
export HTTPS_PROXY="http://user-country-US:your-password@gate.proxyhat.com:8080"

# Deno: теперь fetch подхватит прокси автоматически
deno run --allow-net --allow-env script.ts

# Bun: аналогично
bun run script.ts

Residential-прокси для целей с высокой защитой

Datacenter-прокси часто блокируются антибот-системами, потому что их IP-диапазоны известны и помечены как «hosting provider». Residential-прокси используют IP реальных устройств, что делает их неотличимыми от обычных пользователей. Это критично для SERP-скрапинга, ценового мониторинга и сбора данных с защищённых сайтов. Подробнее — на странице use-case веб-скрапинга.

Следующий пример показывает ротацию пула sticky-сессий с concurrent-запросами через Promise.all и AbortController для таймаута:

// deno run --allow-net concurrent_pool.ts
// или: bun run concurrent_pool.ts

const PASSWORD = "your-password";
const TARGET = "https://httpbin.org/ip";
const TIMEOUT_MS = 10_000;
const POOL_SIZE = 10;

interface FetchResult {
  session: string;
  ip: string | null;
  status: number | null;
  error: string | null;
}

async function fetchWithProxy(sessionId: string): Promise<FetchResult> {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);

  const proxyUrl = `http://user-country-US-session-${sessionId}:${PASSWORD}@gate.proxyhat.com:8080`;

  try {
    // Для Deno — используем createHttpClient; для Bun — опцию proxy в fetch
    const isDeno = typeof Deno !== "undefined";

    let res: Response;
    if (isDeno) {
      const client = Deno.createHttpClient({
        proxy: {
          url: "http://gate.proxyhat.com:8080",
          basicAuth: {
            username: `user-country-US-session-${sessionId}`,
            password: PASSWORD,
          },
        },
      });
      res = await fetch(TARGET, { client, signal: controller.signal });
    } else {
      res = await fetch(TARGET, {
        proxy: proxyUrl,
        signal: controller.signal,
      });
    }

    const data = await res.json();
    return { session: sessionId, ip: data.origin, status: res.status, error: null };
  } catch (err) {
    return {
      session: sessionId,
      ip: null,
      status: null,
      error: err instanceof Error ? err.message : String(err),
    };
  } finally {
    clearTimeout(timeout);
  }
}

// Запускаем 10 concurrent-запросов с разными sticky-сессиями
const sessions = Array.from({ length: POOL_SIZE }, (_, i) => `pool-${i}`);
const results = await Promise.all(sessions.map(fetchWithProxy));

const successCount = results.filter((r) => r.error === null).length;
console.log(`Успешно: ${successCount}/${POOL_SIZE}`);
for (const r of results) {
  console.log(`  Сессия ${r.session}: IP=${r.ip}, status=${r.status}, error=${r.error}`);
}

Этот паттерн даёт 100 concurrent sessions с уникальными IP, каждая со своим sticky-идентификатором. Если сайт блокирует один IP — остальные продолжают работать. Таймаут через AbortController предотвращает зависшие запросы.

Production-советы: ретраи, CA-сертификаты, connection reuse

Ретраи с экспоненциальным backoff

// Универсальный retry-обёртка для Deno и Bun

const MAX_RETRIES = 3;
const BASE_DELAY_MS = 500;

async function fetchWithRetry(
  url: string,
  proxyConfig: { url: string; basicAuth: { username: string; password: string } },
): Promise<Response> {
  let lastError: Error | null = null;

  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
    try {
      const isDeno = typeof Deno !== "undefined";

      if (isDeno) {
        const client = Deno.createHttpClient({ proxy: proxyConfig });
        const res = await fetch(url, { client });
        if (res.status === 429 || res.status >= 500) {
          throw new Error(`HTTP ${res.status}`);
        }
        return res;
      } else {
        const proxyStr = `http://${proxyConfig.basicAuth.username}:${proxyConfig.basicAuth.password}@${proxyConfig.url.replace("http://", "")}`;
        const res = await fetch(url, { proxy: proxyStr });
        if (res.status === 429 || res.status >= 500) {
          throw new Error(`HTTP ${res.status}`);
        }
        return res;
      }
    } catch (err) {
      lastError = err instanceof Error ? err : new Error(String(err));
      const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 100;
      console.warn(`Попытка ${attempt + 1} не удалась: ${lastError.message}. Ждём ${Math.round(delay)}ms`);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }

  throw lastError ?? new Error("Неизвестная ошибка");
}

// Использование
const res = await fetchWithRetry("https://httpbin.org/ip", {
  url: "http://gate.proxyhat.com:8080",
  basicAuth: { username: "user-country-FR-session-retry-1", password: "pass" },
});
console.log(await res.json());

Custom CA через Deno caCerts

Deno позволяет передать CA-сертификаты напрямую в createHttpClient — полезно для корпоративных сред с MITM-прокси:

// deno run --allow-net --read ca_certs.ts

const caCert = await Deno.readTextFile("./custom-ca.pem");

const client = Deno.createHttpClient({
  proxy: {
    url: "http://gate.proxyhat.com:8080",
    basicAuth: { username: "user-country-US", password: "pass" },
  },
  caCerts: [caCert],
});

const res = await fetch("https://httpbin.org/ip", { client });
console.log(await res.json());

ProxyHat Node SDK: сравнение side-by-side

ProxyHat предоставляет Node SDK, который работает в обоих рантаймах — Deno и Bun поддерживают npm-пакеты. SDK абстрагирует управление прокси, ротацию и обработку ошибок. Документация доступна на docs.proxyhat.com.

// Установка: npm install @proxyhat/node-sdk (или deno add / bun add)
// deno run --allow-net sdk_example.ts (или bun run sdk_example.ts)

import { ProxyHat } from "@proxyhat/node-sdk";

const ph = new ProxyHat({
  username: "your-username",
  password: "your-password",
  defaultCountry: "US",
});

// Простой запрос
const res = await ph.fetch("https://httpbin.org/ip");
console.log("SDK IP:", (await res.json()).origin);

// С geo-targeting и sticky-сессией
const res2 = await ph.fetch("https://httpbin.org/ip", {
  country: "DE",
  city: "berlin",
  session: "checkout-flow-42",
});
console.log("DE Berlin IP:", (await res2.json()).origin);

// Concurrent-запросы с автоматической ротацией
const urls = [
  "https://httpbin.org/ip",
  "https://httpbin.org/headers",
  "https://httpbin.org/user-agent",
];

const results = await Promise.all(
  urls.map((url) => ph.fetch(url, { country: "JP", rotate: true })),
);

for (const r of results) {
  console.log(r.status, await r.text());
}
ПодходСтрок кодаКонтрольАбстракция
Raw fetch + Deno.createHttpClient~10ПолныйНизкая
Raw fetch + Bun proxy~5ПолныйНизкая
ProxyHat Node SDK~3СреднийВысокая

SDK удобен для быстрого старта, но raw-подход даёт больше контроля — например, для custom CA-сертификатов или нестандартных таймаутов. Узнать больше о доступных локациях можно на странице локаций ProxyHat.

Этический скрапинг и правовые аспекты

Использование прокси — это инструмент, а не лицензия на сбор любых данных. Несколько принципов:

  • Сначала проверьте официальный API. Многие платформы (Google, Amazon, Twitter/X) предлагают API с легальным доступом к данным. Если API покрывает ваши нужды — используйте его.
  • Публичные данные только. В США Computer Fraud and Abuse Act (CFAA) регулирует доступ к компьютерным системам. Скрапинг публично доступных данных (без авторизации) обычно законен, но обход paywall или access control может нарушать CFAA.
  • GDPR в ЕС. Сбор персональных данных граждан ЕС требует правового основания. Прокси не делают сбор данных легальным — они лишь скрывают ваш IP.
  • Соблюдайте robots.txt. Это не закон, но индустриальный стандарт. Уважайте Crawl-delay и Disallow директивы.
  • Rate limiting. Даже с 100 concurrent sessions, не перегружайте целевой сервер. Адекватный rate limit — 1–5 запросов в секунду на домен.

Для SERP-скрапинга и ценового мониторинга residential-прокси — стандартный инструмент. Подробнее — на странице SERP tracking.

Key Takeaways

  • fetch() не поддерживает прокси по умолчанию — ни в Deno, ни в Bun. Нужна явная конфигурация.
  • Deno: используйте Deno.createHttpClient({ proxy: { url, basicAuth } }) и передавайте клиент в fetch(url, { client }).
  • Bun: используйте fetch(url, { proxy: 'http://user:pass@gate.proxyhat.com:8080' }) — одна строка.
  • Geo-targeting и sticky-сессии кодируются в username: user-country-US-session-abc123.
  • SOCKS5 доступен на порту 1080 через URL-схему socks5://.
  • Per-client конфигурация предпочтительнее HTTP_PROXY env-var в production — больше контроля и гибкости.
  • Residential-прокси необходимы для сайтов с антибот-защитой; datacenter-IP часто блокируются.
  • Ретраи с backoff, AbortController таймауты и connection reuse — обязательны в production.
  • ProxyHat Node SDK работает в обоих рантаймах и упрощает ротацию и geo-targeting.
  • Этика важна: сначала официальный API, публичные данные только, уважайте robots.txt и GDPR.

Часто задаваемые вопросы

Что такое использование прокси в Deno и Bun?

Использование прокси в Deno и Bun — это явная настройка HTTP/SOCKS5-прокси в современных JavaScript-рантаймах. Поскольку стандартный fetch() не поддерживает прокси по умолчанию, Deno решает это через Deno.createHttpClient с опцией proxy, а Bun — через опцию proxy прямо в fetch(). Это позволяет маршрутизировать запросы через residential, mobile или datacenter-прокси для скрапинга, ценового мониторинга и SERP-tracking.

Почему использование прокси в Deno и Bun важно для разработчиков?

Потому что fetch() в обоих рантаймах игнорирует прокси по умолчанию. Без явной конфигурации все запросы идут с вашего реального IP, что приводит к блокировкам при скрапинге, ценовом мониторинге или SERP-tracking. Правильная настройка прокси через Deno.createHttpClient или Bun fetch proxy позволяет распределять запросы по разным IP, geo-таргетам и sticky-сессиям, повышая success rate до 99% и выше.

Какой тип прокси лучше всего подходит для Deno и Bun?

Residential-прокси — лучший выбор для большинства задач скрапинга в Deno и Bun. Они используют IP реальных устройств, что делает их неотличимыми от обычных пользователей и обходит антибот-системы. Datacenter-прокси быстрее и дешевле, но чаще блокируются. Mobile-прокси — самые надёжные, но дороже. Для разработки и тестирования подойдут datacenter-прокси, а для production-скрапинга защищённых сайтов — residential.

Как избежать блокировок при использовании прокси в Deno и Bun?

Используйте residential-прокси с ротацией IP, кодируйте geo-targeting и sticky-сессии в username (user-country-US-session-abc123), ограничивайте concurrency до 1–5 запросов в секунду на домен, реализуйте ретраи с экспоненциальным backoff, используйте AbortController для таймаутов, и соблюдайте robots.txt. Также проверьте, есть ли у целевого сайта официальный API — это может полностью заменить скрапинг.

Готовы начать?

Резидентные, ISP и мобильные прокси в 148+ странах. Создайте бесплатный аккаунт.

Создать бесплатный аккаунт
← Вернуться в Блог