WeProxy

Руководство

Прокси в Node.js: примеры для axios, undici и fetch

12 мин чтения

В Node.js прокси задаётся через NODE_USE_ENV_PROXY для встроенного fetch, через ProxyAgent в undici и настройкой proxy в axios. Примеры HTTP и SOCKS5, управление sticky-сессиями, проверка выходного IP и ошибки прокси в разных клиентах.

Коротко

  • Node.js сам не читает настройки прокси; встроенный fetch использует переменные HTTP_PROXY, HTTPS_PROXY и NO_PROXY, только если процесс запущен с NODE_USE_ENV_PROXY=1 или --use-env-proxy (Node.js 24 и версии после 22.21).
  • В undici прокси передаётся в запрос как dispatcher: new ProxyAgent("http://USER:PASSWORD@host:port"); функцию fetch нужно импортировать из того же пакета undici, иначе разница версий может привести к ошибке запроса.
  • axios принимает прокси настройкой proxy: { protocol, host, port, auth }; в объекте auth пароль пишется без кодирования. При использовании https-proxy-agent добавляют proxy: false, чтобы не было конфликта с переменными окружения.
  • Для SOCKS5 в axios и node:https используется socks-proxy-agent, в undici — экспериментальный Socks5ProxyAgent; схема socks5h оставляет разрешение DNS на стороне прокси.
  • undici хранит настоящую причину ошибки в err.cause; при 407 не повторяйте запрос, а проверьте, что пароль закодирован через encodeURIComponent и что пакет активен.

В Node.js прокси обычно подключают одним из трёх способов: для встроенного fetch запускают Node с NODE_USE_ENV_PROXY=1 и задают переменную HTTPS_PROXY, в undici передают в запрос класс ProxyAgent как dispatcher, а в axios используют настройку proxy. Адрес прокси во всех случаях записывается как http://USER:PASSWORD@host:port; для SOCKS5 нужен socks-proxy-agent или класс Socks5ProxyAgent из undici.

Какой способ выбрать, зависит от вашего HTTP-клиента. Node.js сам по себе не читает настройки прокси для http.request и fetch: нужно либо включить поддержку переменных окружения, либо отдельно передать прокси каждому клиенту. В примерах используется адрес шлюза WeProxy gw.weproxy.com.tr:8989; USER и PASSWORD — ваши данные из личного кабинета.

Как записать адрес прокси в Node.js?

Все клиенты принимают прокси в одном и том же формате URL:

схема://пользователь:пароль@host:port

Схема — это протокол самого прокси-сервера. Даже если целевой сайт работает по HTTPS, адрес HTTP-прокси начинается с http://: клиент отправляет прокси запрос CONNECT, и TLS-соединение устанавливается внутри этого туннеля. Если записать адрес с https://, клиент попытается говорить по TLS с самим прокси, и undici выдаст ошибку ERR_SSL_WRONG_VERSION_NUMBER. Чем HTTP-прокси отличается от SOCKS5, объясняет статья HTTP vs SOCKS5.

Если в пароле есть @, :, / или #, перед вставкой в URL закодируйте его через encodeURIComponent. Незакодированный @ ломает разбор адреса, и undici выдаёт TypeError: Invalid URL:

const user = encodeURIComponent(process.env.WEPROXY_USERNAME);
const pass = encodeURIComponent(process.env.WEPROXY_PASSWORD);
export const PROXY = `http://${user}:${pass}@gw.weproxy.com.tr:8989`;

Храните учётные данные в переменной окружения или менеджере секретов, тогда пароль не попадёт в Git вместе с кодом. Если в личном кабинете вы получили список в формате ip:port:user:pass, конвертер форматов прокси переведёт строки в формат URL. Про IPv6-адреса и схему socks5h подробно рассказано в руководстве по форматам прокси.

Как использовать прокси со встроенным fetch в Node.js?

В Node.js 24 и в версиях после 22.21 встроенный fetch умеет читать переменные HTTP_PROXY, HTTPS_PROXY и NO_PROXY. По умолчанию эта возможность выключена; её включают переменной NODE_USE_ENV_PROXY=1 или флагом --use-env-proxy. Без флага Node игнорирует эти переменные, и запрос уходит напрямую с вашего IP.

export HTTPS_PROXY="http://USER:[email protected]:8989"
export HTTP_PROXY="$HTTPS_PROXY"
export NO_PROXY="localhost,127.0.0.1"
NODE_USE_ENV_PROXY=1 node app.mjs

В PowerShell то же самое делается так:

$env:HTTPS_PROXY = "http://USER:[email protected]:8989"
$env:HTTP_PROXY = $env:HTTPS_PROXY
node --use-env-proxy app.mjs

В самом app.mjs о прокси нет ни строчки:

const res = await fetch('https://api.ipify.org?format=json', {
  signal: AbortSignal.timeout(30_000),
});
console.log(await res.json()); // { ip: '<выходной IP прокси>' }

В актуальных версиях этот флаг перенаправляет через прокси и глобальные агенты модулей node:http и node:https, так что вызовы в старом стиле вроде https.get тоже идут через прокси без изменений в коде. Подробности есть в документации Node.js CLI. Способ удобен, когда существующее приложение нужно пустить через прокси, не трогая код. Весь процесс использует один прокси; если для разных запросов нужны разные прокси, переходите к следующему способу.

Как использовать прокси через undici ProxyAgent?

undici — HTTP-клиент, на котором построена реализация fetch в Node.js; в npm он публикуется и как отдельный пакет. Класс ProxyAgent задаёт прокси на уровне запроса:

npm install undici
import { ProxyAgent, fetch } from 'undici';
import { PROXY } from './proxy.mjs';

const dispatcher = new ProxyAgent(PROXY);

const res = await fetch('https://api.ipify.org?format=json', {
  dispatcher,
  signal: AbortSignal.timeout(30_000),
});
console.log(await res.json());

await dispatcher.close();

Импортируйте fetch из того же пакета, что и ProxyAgent. Версия undici внутри Node может отставать от версии в npm. Если передать встроенному fetch из Node.js 24 объект ProxyAgent из undici 8, установленного через npm, запрос падает с fetch failed, а исходной причиной будет invalid onRequestStart method.

Если не хотите вшивать пароль в URL, учётные данные можно передать значением заголовка. В таком виде процентное кодирование не нужно:

const basic = Buffer.from(
  `${process.env.WEPROXY_USERNAME}:${process.env.WEPROXY_PASSWORD}`
).toString('base64');

const dispatcher = new ProxyAgent({
  uri: 'http://gw.weproxy.com.tr:8989',
  token: `Basic ${basic}`,
});

Чтобы пустить через прокси все вызовы fetch в приложении, используйте setGlobalDispatcher(new ProxyAgent(PROXY)). Есть и вариант, который читает переменные окружения без флага процесса: EnvHttpProxyAgent сам берёт значения HTTPS_PROXY и NO_PROXY. Полный список параметров приведён в документации undici ProxyAgent.

Как использовать прокси в axios?

В Node.js axios получает прокси через настройку proxy. Учётные данные передаются в отдельных полях, поэтому спецсимволы кодировать не нужно:

import axios from 'axios';

const client = axios.create({
  proxy: {
    protocol: 'http',
    host: 'gw.weproxy.com.tr',
    port: 8989,
    auth: {
      username: process.env.WEPROXY_USERNAME,
      password: process.env.WEPROXY_PASSWORD,
    },
  },
  timeout: 30_000,
});

const { data } = await client.get('https://api.ipify.org?format=json');
console.log(data);

Актуальные версии axios открывают через прокси туннель CONNECT для HTTPS-целей. В версии 1.16.1 исправили ошибку, из-за которой в некоторых конфигурациях содержимое HTTPS-запроса могло уходить на HTTP-прокси в открытом виде (журнал изменений axios). Если у вас старая версия, обновите axios или поручите туннель пакету https-proxy-agent:

import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { PROXY } from './proxy.mjs';

const client = axios.create({
  httpsAgent: new HttpsProxyAgent(PROXY),
  proxy: false,
});

По умолчанию axios читает переменные HTTPS_PROXY и NO_PROXY; без строки proxy: false прокси из окружения может конфликтовать с вашим агентом. А значение HTTPS_PROXY с паролем в процентной кодировке axios отправляет прокси как есть, в закодированном виде, и в ответ приходит 407. Если в пароле есть спецсимволы, в axios используйте объект auth.

Как использовать SOCKS5-прокси в Node.js?

Ни встроенный fetch, ни axios сами по себе SOCKS5 не поддерживают. Для axios и node:https используется пакет socks-proxy-agent:

npm install socks-proxy-agent
import axios from 'axios';
import { SocksProxyAgent } from 'socks-proxy-agent';

const user = encodeURIComponent(process.env.WEPROXY_USERNAME);
const pass = encodeURIComponent(process.env.WEPROXY_PASSWORD);
const agent = new SocksProxyAgent(`socks5h://${user}:${pass}@gw.weproxy.com.tr:8989`);

const client = axios.create({ httpAgent: agent, httpsAgent: agent, proxy: false });
console.log((await client.get('https://api.ipify.org')).data);

Схема socks5h оставляет разрешение доменного имени прокси; со схемой socks5 домен цели разрешается через ваш собственный DNS-сервер. Если вы работаете с undici, ту же задачу решает класс Socks5ProxyAgent, и он всегда разрешает домен на стороне прокси. Класс пока помечен как экспериментальный и при первом использовании выводит ExperimentalWarning:

import { Socks5ProxyAgent, fetch } from 'undici';

const dispatcher = new Socks5ProxyAgent(`socks5://${user}:${pass}@gw.weproxy.com.tr:8989`);
const res = await fetch('https://api.ipify.org', { dispatcher });
console.log(await res.text());

Поддержка SOCKS5 зависит от пакета. Rotating Residential IPv4 работает по HTTP и SOCKS5, а Mobile 5G Proxy на реальных SIM-картах — только по HTTP. UDP-трафик вроде игр и VoIP через эти клиенты не передаётся; конфигурации, где нужен UDP, разобраны в руководстве по UDP-прокси.

Сравнение axios, undici и fetch

ХарактеристикаВстроенный fetchundiciaxios
УстановкаНе нужна (Node.js 18+)npm install undicinpm install axios
Как задаётся проксиNODE_USE_ENV_PROXY=1 + HTTPS_PROXYdispatcher: new ProxyAgent(...)proxy: {...} или httpsAgent
Разные прокси для разных запросовНет, один на процессЕстьЕсть
Учётные данныеВ URLВ URL или tokenОбъект auth
SOCKS5НетSocks5ProxyAgent (экспериментальный)socks-proxy-agent
Переменные окруженияТолько с флагомЧерез EnvHttpProxyAgentВключены по умолчанию
Для каких задачПустить через прокси, не трогая кодНовый код, много прокси, высокая параллельностьСуществующие проекты на axios

В новом проекте undici одним пакетом даёт прокси на уровне запроса, SOCKS5 и настройку пула соединений. Если кодовая база построена на axios, менять клиент незачем: хватит настройки proxy или https-proxy-agent.

Как управлять rotating и sticky IP в Node.js?

У rotating-шлюза адрес прокси не меняется, а выходной IP берётся из пула. В Node.js нужно следить за переиспользованием соединений. undici ProxyAgent использует открытый для цели туннель и в следующих запросах, а с Node.js 19 в глобальных агентах http и https по умолчанию включён keepAlive. Если смена IP происходит на новом соединении, запросы подряд через один агент могут выходить с одного IP. Чтобы понять, меняет ли шлюз IP на каждый запрос или на каждое соединение, запросите api.ipify.org дважды через один агент, затем дважды через два разных агента.

Логину, корзине или многошаговой форме, наоборот, нужен один и тот же IP на протяжении всех запросов. В резидентном и мобильном пулах WeProxy для этого в имя пользователя добавляется идентификатор сессии. Формат имени пользователя и длительность сессии зависят от продукта, поэтому берите и то и другое из личного кабинета. В коде достаточно открыть отдельный агент для каждого аккаунта:

import { ProxyAgent, fetch } from 'undici';

// Имена пользователей копируются из кабинета; у каждого профиля свой session ID.
const PROFILES = {
  'account-1': 'USER_WITH_SESSION_1',
  'account-2': 'USER_WITH_SESSION_2',
};
const pass = encodeURIComponent(process.env.WEPROXY_PASSWORD);

const agents = Object.fromEntries(
  Object.entries(PROFILES).map(([name, user]) => [
    name,
    new ProxyAgent(`http://${encodeURIComponent(user)}:${pass}@gw.weproxy.com.tr:8989`),
  ])
);

for (const [name, dispatcher] of Object.entries(agents)) {
  const res = await fetch('https://api.ipify.org', { dispatcher });
  console.log(name, await res.text());
}

Два профиля не должны делить один session ID, иначе целевой сайт увидит двух разных пользователей с одного IP. Каким задачам нужен sticky, а каким rotating, показано в таблице сценариев в руководстве sticky vs rotating. Пакет, где есть оба типа сессий, описан на странице ротационных резидентных прокси.

Если нужно обойти много URL, ограничьте параллельность вместо того, чтобы запускать всё сразу через Promise.all. Небольшой пул без дополнительных пакетов:

async function runPool(items, limit, worker) {
  const results = [];
  let next = 0;
  const lanes = Array.from({ length: limit }, async () => {
    while (next < items.length) {
      const i = next++;
      results[i] = await worker(items[i]).catch((err) => err);
    }
  });
  await Promise.all(lanes);
  return results;
}

const urls = Array(20).fill('https://api.ipify.org');
const ips = await runPool(urls, 5, async (url) =>
  (await fetch(url, { dispatcher: agents['account-1'] })).text()
);

Предел здесь задаёт целевой сайт: резкий всплеск запросов с одного IP упирается в его лимит частоты и получает 429.

Как проверить, что прокси работает?

Самый короткий тест — запросить выходной IP без прокси и через прокси и сравнить:

import { ProxyAgent, fetch as undiciFetch } from 'undici';
import { PROXY } from './proxy.mjs';

const dispatcher = new ProxyAgent(PROXY);
const direct = await (await fetch('https://api.ipify.org')).text();
const viaProxy = await (await undiciFetch('https://api.ipify.org', { dispatcher })).text();

console.log({ direct, viaProxy });
if (direct === viaProxy) throw new Error('Запрос не проходит через прокси');

Если значения совпадают, настройка прокси не доходит до запроса. Со встроенным fetch проверьте флаг NODE_USE_ENV_PROXY, в axios — конфликт с переменными окружения (proxy: false), а в любом случае — список NO_PROXY. Страну и сеть IP прокси покажет инструмент My IP. Проверить сам прокси независимо от кода можно в proxy checker: если там прокси работает, а из Node нет, проблема в настройках клиента. Если удобнее проверить то же самое из терминала, подойдут команды из статьи о прокси в cURL.

Ошибки прокси в Node.js и их решение

Одна и та же проблема в каждом клиенте выглядит как своя ошибка. Соответствия в таблице получены на локальном тестовом прокси с Node.js 24, undici 8, axios 1.21 и https-proxy-agent 9:

Ситуацияundici fetchaxiosnode:https + HttpsProxyAgent
Учётные данные отклонены (407)fetch failed; исходная причина «Proxy response (407) !== 200 when HTTP Tunneling»AxiosError, status: 407Ошибки нет, statusCode ответа равен 407
Не удалось подключиться к проксиfetch failed; причина ECONNREFUSEDECONNREFUSEDСобытие error, ECONNREFUSED
Истёк таймаутTimeoutError через AbortSignal.timeoutECONNABORTEDЛовится через req.setTimeout
Адрес прокси записан с https://ERR_SSL_WRONG_VERSION_NUMBEREPROTO («wrong version number»)EPROTO («wrong version number»)
Пароль не закодированTypeError: Invalid URLС объектом auth проблем нетTypeError: Invalid URL

undici хранит настоящую причину ошибки в err.cause; если выводить только err.message, любая ошибка будет выглядеть как «fetch failed». Пишите в лог также значения err.cause?.code и err.cause?.message.

Получив 407, сначала проверьте, что пароль закодирован, а затем — что пакет активен. Если вы работаете через whitelist по IP, выходной IP сервера, на котором запущен код, должен быть добавлен в личный кабинет; если скрипт, работавший локально, после переноса на сервер начал получать 407, причина именно в этом. Какие способы доступны в каких продуктах, описано в руководстве по аутентификации прокси. Не повторяйте запрос при 407: с теми же учётными данными результат не изменится.

А вот 429, 502, 503 и 504 бывают временными и требуют повторов с растущей задержкой. На каком уровне возникает каждый код, объясняет руководство по кодам ошибок прокси. Если соединение постоянно падает по таймауту, возможно, корпоративная сеть или облачный провайдер закрывает исходящие соединения на порт 8989. При ошибке сертификата не отключайте проверку через NODE_TLS_REJECT_UNAUTHORIZED=0: эта настройка выключает проверку TLS во всём процессе.

Соответствие правилам и безопасность при работе с прокси в Node.js

Если вы пишете скрипт для сбора данных, соблюдайте правила robots.txt цели (RFC 9309) и её условия использования, даже если запросы идут через прокси, и держите частоту запросов на уровне, который сайт выдержит. При обработке персональных данных обязательства по KVKK и GDPR действуют независимо от использования прокси. Разрешённые и запрещённые варианты использования сервисов WeProxy перечислены на странице правил допустимого использования.

URL прокси содержит пароль, поэтому он легко утекает в сообщения об ошибках и логи. Перед записью в лог разберите адрес через new URL(PROXY) и удалите поле password. Шаги настройки собраны на странице интеграции с Node.js, а готовый к запуску пример проекта лежит в репозитории weproxy-io/nodejs-proxy. Для автоматизации браузера смотрите интеграцию с Puppeteer, а как сделать то же самое на Python, рассказывает статья о прокси в Python. Как выбрать тип IP и режим сессии для скрапинга, описано на странице веб-скрапинга.

Частые вопросы

Как использовать прокси с fetch в Node.js?

В Node.js 24 и версиях после 22.21 задайте переменной HTTPS_PROXY значение http://USER:PASSWORD@host:port и запустите приложение с NODE_USE_ENV_PROXY=1 или командой node --use-env-proxy; запросы встроенного fetch пойдут через прокси. Если для разных запросов нужны разные прокси, установите пакет undici из npm, передайте объект ProxyAgent через параметр dispatcher и импортируйте fetch тоже из undici.

Почему HTTPS_PROXY задан, а Node.js использует мой реальный IP?

По умолчанию Node.js не читает прокси из переменных окружения. Встроенному fetch и node:https нужен NODE_USE_ENV_PROXY=1 или флаг --use-env-proxy; в старых версиях такой поддержки нет. Кроме того, список NO_PROXY может включать домен цели. Запросите api.ipify.org через прокси и без него и сравните результаты: так проблема находится быстрее всего.

Как задать в axios прокси с логином и паролем?

В axios.create передайте в настройку proxy поля protocol: "http", host, port и auth: { username, password }. В объекте auth пароль пишется как есть, процентное кодирование не нужно. В старых версиях axios для HTTPS-запросов надёжнее передать пакет https-proxy-agent как httpsAgent и добавить proxy: false; также рекомендуется держать axios в актуальной версии.

Как использовать SOCKS5-прокси в Node.js?

Встроенный fetch и axios сами по себе SOCKS5 не поддерживают. Для axios или node:https установите пакет socks-proxy-agent, создайте агент с адресом socks5h://USER:PASSWORD@host:port и передайте его в поля httpAgent и httpsAgent. Если вы работаете с undici, ту же задачу решает экспериментальный класс Socks5ProxyAgent. Поддержка SOCKS5 зависит от пакета: Mobile 5G Proxy работает только по HTTP.

Почему undici выдаёт только ошибку «fetch failed»?

undici fetch выбрасывает сетевые ошибки как общий TypeError, а настоящую причину хранит в поле err.cause. Например, при 407 в конце цепочки будет сообщение «Proxy response (407) !== 200 when HTTP Tunneling», а если прокси недоступен — код ECONNREFUSED. Выводите в логи также err.cause?.code и err.cause?.message, различайте по ним тип ошибки и повторяйте запрос только при временных ошибках.

Начать

Проверьте Прокси Уже Сегодня

Проверьте связность бесплатными инструментами или выберите пакет с продажами.