← Bloga geri dön

Node.js'de Undici ProxyAgent: HTTP/SOCKS5 Proxy Ayarları ve IP Döngüsü için Gereksiz Bağımlılıklar Olmadan

Node.js'te HTTP ve SOCKS5 proxy ile çalışmak için yerleşik undici ProxyAgent'in nasıl kullanılacağını, IP döngüsünü nasıl organize edeceğimizi ve https-proxy-agent ile socks-proxy-agent kullanmadan nasıl yapacağımızı inceliyoruz.

📅18 Ekim 2026

Node.js'deki proxy ile ilgili çoğu kılavuz, projeye https-proxy-agent, socks-proxy-agent ve birkaç ek paket eklemeyi öneriyor. Ancak, Node.js 18+ sürümünden itibaren yerleşik HTTP istemcisi undici, çoğu proxy ile API'yi analiz etme ve test etme görevini yerine getiren ProxyAgent içeriyor. Bu yazıda, undici ile HTTP/HTTPS tünelleme nasıl ayarlanır, istekler arasında IP rotasyonu nasıl yapılır ve SOCKS5'e ihtiyaç duyulursa ek bir bağımlılık yüklemek istemiyorsanız ne yapmanız gerektiğini inceleyeceğiz.

Neden undici, axios ve request yerine

undici, Node.js ekibi tarafından yazılmış bir HTTP/1.1 ve HTTP/2 istemcisidir ve 18. sürümden itibaren global fetch'in temelini oluşturur. Geçişli bağımlılıkları yoktur, çoğu bağlantı havuzlama benchmark'ında axios'tan daha hızlıdır ve konumuz açısından önemli olan, HTTP CONNECT tünelleme ile proxy ile çalışma için yerel bir modülü vardır.

Wildberries, Ozon veya farklı coğrafyalardan reklam yaratıcılarını test ederken, node_modules içindeki her ek paket, Node.js güncellemeleri sırasında potansiyel bir arıza noktasıdır. undici, çalışma zamanı ile birlikte gelir, bu nedenle güncel TLS, HTTP/2 ve keep-alive uygulaması alırsınız; bu, terkedilmiş bir kütüphanenin yeni bir ana sürümde bozulma riski olmadan.

Kurulumu son derece basittir:

npm install undici

Eğer Node.js 20+ sürümüne sahipseniz, API'nin bir kısmı global olarak içe aktarma olmadan kullanılabilir, ancak ProxyAgent için yine de içe aktarma gereklidir, çünkü varsayılan olarak global fetch'te dışa aktarılmaz.

ProxyAgent için temel ayar

Proxy üzerinden istek yapmak için minimum çalışan örnek şöyle görünür:

import { ProxyAgent, fetch } from 'undici';

const proxyAgent = new ProxyAgent('http://proxy.example.com:8080');

const response = await fetch('https://httpbin.org/ip', {
  dispatcher: proxyAgent,
});

const data = await response.json();
console.log(data);

Anahtar nokta — dispatcher parametresidir. Bu, eski http modülündeki agent seçeneği değil, undici'nin, TLS el sıkışmasından önce bağlantıyı yakalayan ve hedef ana bilgisayara CONNECT yöntemi ile tünel kuran bir yöneticisidir. HTTPS istekleri için proxy üzerinden bu kritik bir durumdur: sıradan bir HTTP proxy, şifreli trafiğin içeriğini göremez, yalnızca TCP tüneli içinde baytları iletir.

Eğer fetch ile değil de düşük seviyeli undici.request ile çalışıyorsanız, sözdizimi benzer:

import { request, ProxyAgent } from 'undici';

const proxyAgent = new ProxyAgent('http://proxy.example.com:8080');

const { statusCode, body } = await request('https://api.example.com/data', {
  dispatcher: proxyAgent,
});

console.log(statusCode);
for await (const chunk of body) {
  console.log(chunk.toString());
}

Yüzlerce isteği dakikada yapan üretim parser'ları için, her istek için yeni bir ProxyAgent oluşturmamak önemlidir — bu, bağlantı havuzlamasını öldürür. Bir kez bağlantı havuzu için bir ajan örneği oluşturun ve çağrılar arasında yeniden kullanın.

Kullanıcı adı ve şifre ile proxy kimlik doğrulaması

Çoğu ücretli proxy sağlayıcısı, IP beyaz listeleme yerine kullanıcı adı/şifre çifti ile erişim sağlar. undici'de kimlik bilgileri doğrudan proxy URL'sine iletilir:

import { ProxyAgent, fetch } from 'undici';

const login = 'user123';
const password = 'pass456';
const proxyUrl = `http://${login}:${password}@gate.proxycove.com:9000`;

const proxyAgent = new ProxyAgent(proxyUrl);

const response = await fetch('https://httpbin.org/ip', {
  dispatcher: proxyAgent,
});

console.log(await response.json());

Dikkat: Eğer kullanıcı adı veya şifre özel karakterler (@, :, /) içeriyorsa, bunları encodeURIComponent ile geçirmelisiniz, aksi takdirde undici içindeki URL ayrıştırıcı bağlantı dizesini yanlış bir şekilde çözümleyecek ve anlamlı bir mesaj yerine 407 kimlik doğrulama hatası alacaksınız.

Oturumun kararlılığının önemli olduğu görevler için — örneğin, bir oturum süresince bir IP'ye bağlı olarak bir pazaryerinde kimlik doğrulama — rezidans proxy'leri kullanmak daha uygundur: IP belirli bir süre değişmez, bu nedenle hizmet "zıplama" görmez ve her istekte yeniden captcha gerektirmez.

İstekler arasında proxy rotasyonu

Eğer N sayıda proxy listeniz varsa ve istekleri bunlar arasında dağıtmak istiyorsanız, en basit seçenek — üçüncü taraf kütüphaneler olmadan round-robin:

import { ProxyAgent, fetch } from 'undici';

const proxyList = [
  'http://user:[email protected]:8000',
  'http://user:[email protected]:8000',
  'http://user:[email protected]:8000',
];

let currentIndex = 0;

function getNextAgent() {
  const proxyUrl = proxyList[currentIndex];
  currentIndex = (currentIndex + 1) % proxyList.length;
  return new ProxyAgent(proxyUrl);
}

async function fetchWithRotation(url) {
  const agent = getNextAgent();
  return fetch(url, { dispatcher: agent });
}

for (let i = 0; i < 10; i++) {
  const res = await fetchWithRotation('https://httpbin.org/ip');
  console.log(await res.json());
}

Bu yaklaşım, her bir sonraki isteğin mantıksal olarak önceki isteğe bağımsız olduğu ürün kartlarını analiz etmek veya fiyatları izlemek için iyi çalışır. Her istekte IP'nin hızlı bir şekilde değişmesi gereken görevler için, sağlayıcı tarafında otomatik rotasyon yapan bir proxy kullanmak daha kolaydır — bu durumda yukarıdaki tüm kod, yalnızca bir statik URL geçidi ile sınırlı hale gelir ve rotasyonu veri merkezi proxy'si veya zamanlayıcıya göre rotasyon yapan bir rezidans havuzu üstlenir.

Eğer sadece dairesel rotasyon değil, ağırlıklı bir rotasyon gerekiyorsa — örneğin, daha hızlı proxy'lere öncelik vermek — veri yapısına ağırlık ekleyin:

const weightedProxies = [
  { url: 'http://user:[email protected]:8000', weight: 5 },
  { url: 'http://user:[email protected]:8000', weight: 1 },
];

function pickWeighted(list) {
  const total = list.reduce((sum, p) => sum + p.weight, 0);
  let rnd = Math.random() * total;
  for (const p of list) {
    if (rnd < p.weight) return p.url;
    rnd -= p.weight;
  }
  return list[0].url;
}

Proxy'nin canlılığını kontrol etme ve "ölü" IP'leri hariç tutma

Proxy havuzu zamanla "bozulur": bazı IP'ler hedef site tarafından yasaklanır, bazıları yanıt vermez. Rotasyonun ölü adresler üzerinden istek göndermemesi için, yerleşik AbortController ile bir sağlık kontrolü ekleyin:

import { ProxyAgent, fetch } from 'undici';

async function isProxyAlive(proxyUrl, timeoutMs = 5000) {
  const agent = new ProxyAgent(proxyUrl);
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const res = await fetch('https://httpbin.org/ip', {
      dispatcher: agent,
      signal: controller.signal,
    });
    return res.status === 200;
  } catch {
    return false;
  } finally {
    clearTimeout(timer);
  }
}

async function filterAliveProxies(proxyList) {
  const checks = await Promise.all(
    proxyList.map(async (url) => ({ url, alive: await isProxyAlive(url) }))
  );
  return checks.filter((c) => c.alive).map((c) => c.url);
}

Bu tür bir kontrolü birkaç dakikada bir arka planda bir zamanlayıcı ile çalıştırın ve aktif havuzu güncelleyin, tüm statik listeyi değil. Bu, özellikle ucuz veri merkezi IP'lerinde kendi proxy'lerinizi tutuyorsanız önemlidir — burada kesilme oranı, yönetilen sağlayıcı havuzlarına göre daha yüksektir.

Üçüncü taraf kütüphaneler olmadan SOCKS5

Önemli bir not: undici'deki yerleşik ProxyAgent, HTTP CONNECT tünellemesi ile çalışır ve "kutudan çıktığı gibi" yalnızca HTTP/HTTPS proxy'lerini anlar. SOCKS5 protokolü farklı bir şekilde çalışır — bu, TCP seviyesinde ikili bir el sıkışmasıdır ve undici bunu yerel olarak çözümlemez. Eğer socks-proxy-agent yüklemeden SOCKS5'e ihtiyacınız varsa, net modülü ve özel bir Agent için connect seçeneği ile minimum bir bağlayıcıyı manuel olarak uygulayabilirsiniz:

import net from 'node:net';
import { Agent, fetch } from 'undici';

function socksHandshake(socket, targetHost, targetPort) {
  return new Promise((resolve, reject) => {
    // Adım 1: selamlaşma, kimlik doğrulama yöntemi yok
    socket.write(Buffer.from([0x05, 0x01, 0x00]));

    socket.once('data', (greeting) => {
      if (greeting[1] !== 0x00) {
        return reject(new Error('SOCKS5: kimlik doğrulama yöntemi desteklenmiyor'));
      }

      const hostBuf = Buffer.from(targetHost, 'utf8');
      const request = Buffer.concat([
        Buffer.from([0x05, 0x01, 0x00, 0x03, hostBuf.length]),
        hostBuf,
        Buffer.from([targetPort >> 8, targetPort & 0xff]),
      ]);

      socket.write(request);

      socket.once('data', (reply) => {
        if (reply[1] !== 0x00) {
          return reject(new Error('SOCKS5: bağlantı reddedildi'));
        }
        resolve(socket);
      });
    });
  });
}

const socksAgent = new Agent({
  connect: (options, callback) => {
    const socket = net.connect(1080, 'socks-proxy.example.com', async () => {
      try {
        await socksHandshake(socket, options.hostname, 443);
        callback(null, socket);
      } catch (err) {
        callback(err, null);
      }
    });
    socket.on('error', (err) => callback(err, null));
  },
});

const response = await fetch('https://httpbin.org/ip', { dispatcher: socksAgent });
console.log(await response.json());

Bu, kullanıcı adı ve şifre ile kimlik doğrulama (0x02 yöntemi) olmadan basitleştirilmiş bir el sıkışmadır ve IPv6 desteği yoktur — üretim için kimlik doğrulama ve farklı adres türlerini işleyen bir dal eklemek daha iyidir. Ancak mekanizmayı anlamak için bu yeterlidir: SOCKS5, el sıkışma aşamasında tünel içinde TLS gerektirmez, bu nedenle kod ilk bakışta göründüğünden daha kısadır.

Pratikte SOCKS5 genellikle web sayfalarını analiz etmek için değil, mobil uygulamaların ve Dolphin Anty veya AdsPower gibi anti-tespit tarayıcılarının trafiğini yönlendirmek için kullanılır; burada protokol, HTTP istekleri yerine soketler düzeyinde çalışmak için gereklidir. Eğer amacınız Node.js'de HTTP/HTTPS trafiği ise, yerleşik ProxyAgent genellikle yeterlidir ve manuel SOCKS5 bağlayıcısıyla uğraşmanın bir anlamı yoktur.

Zaman aşımı işleme ve yeniden deneme mantığı

Proxy'ler, doğrudan bağlantılara göre daha sık bağlantıları keser — bu, residential ağlar için normaldir, burada bir düğüm her an çevrimdışı olabilir. Üstel gecikme ile bir yeniden deneme sarmalayıcısına ihtiyacınız var:

import { ProxyAgent, fetch } from 'undici';

async function fetchWithRetry(url, proxyList, maxRetries = 3) {
  let lastError;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const proxyUrl = proxyList[attempt % proxyList.length];
    const agent = new ProxyAgent(proxyUrl);
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 8000);

    try {
      const res = await fetch(url, { dispatcher: agent, signal: controller.signal });
      clearTimeout(timer);
      if (res.status >= 500) throw new Error(`Sunucu hatası: ${res.status}`);
      return res;
    } catch (err) {
      clearTimeout(timer);
      lastError = err;
      const delay = 500 * 2 ** attempt;
      await new Promise((r) => setTimeout(r, delay));
    }
  }

  throw new Error(`Tüm denemeler tükendi: ${lastError?.message}`);
}

Bu sarmalayıcı, hatalar sırasında rotasyon sorununu da çözer: eğer mevcut proxy kesilirse, bir sonraki deneme otomatik olarak listedeki başka bir IP'ye geçer, aynı ölü düğüme takılmaz.

undici ProxyAgent, https-proxy-agent ve socks-proxy-agent karşılaştırması

Araç Bağımlılıklar SOCKS5 Desteği HTTP/2 Hız
undici ProxyAgent yok (Node 18+ içinde yerleşik) kutudan çıktığı gibi yok evet yüksek
https-proxy-agent 1-2 paket hayır hayır orta
socks-proxy-agent 3-4 paket evet hayır orta

Minimum bağımlılık sayısıyla saf HTTP/HTTPS analizi için undici ProxyAgent, yerleşik olması ve HTTP/2 ile uyumluluğu sayesinde avantaj sağlar. Üretimde SOCKS5 için, kendi el yapımı el sıkışmanızı desteklemektense, kanıtlanmış socks-proxy-agent almak daha kolaydır — yukarıdaki bölümdeki manuel uygulama, eğitim ve nadir kenar durumları için iyidir, ancak saatte binlerce bağlantı ile yoğun bir üretim için değil.

Pratik kullanım senaryoları: analiz ve coğrafi test

Pazaryerlerinde fiyat izleme. Yukarıdaki bölümlerdeki proxy rotasyonu ve yeniden deneme mantığı ile undici'de bir script, Wildberries ve Ozon'daki rate-limit'i aşmak için mükemmel bir şekilde uygundur: her bir ürün kartı isteği yeni bir IP ile gider ve sağlık kontrolü otomatik olarak yasaklı adresleri havuzdan hariç tutar.

Coğrafi olarak reklam yaratıcılarını test etme. Belirli bir ülkeden bir kullanıcıya içerik nasıl sunuluyor kontrol edilmek isteniyorsa, ProxyAgent'te proxy URL'sini ilgili bölge düğümüne değiştirmek yeterlidir — API veya sunucu HTML yanıtı üzerinden kontrol yapılıyorsa, herhangi bir tarayıcı başlatmaya gerek yoktur.

Farklı ülkelerde içerik erişilebilirliğini kontrol etme. Hedef sunucu IP'ye bağlı olarak farklı içerikler sunuyorsa, rezidans bağlantıları hedef sunucu için normal bir kullanıcıdan ayırt edilemez görünür, bu da veri merkezi aralıklarına göre engellenme riskini azaltır. Bu senaryo, hedef hizmetin ağ türünü kontrol edip belirgin veri merkezi alt ağlarını engellediği mobil proxy'ler için özellikle geçerlidir.

API uç noktalarının toplu kontrolü. Kendi API'nizi dış proxy havuzu üzerinden yük testi yaparken, bağlantı havuzlamasına dikkat etmek önemlidir — undici varsayılan olarak bir ProxyAgent içinde TCP bağlantılarını yeniden kullanır, bu da bir ana bilgisayara yapılan yüzlerce ardışık istekte TLS el sıkışmalarında zaman tasarrufu sağlar.

Sonuç

Yerleşik undici ProxyAgent, Node.js'de HTTP/HTTPS proxy ile ilgili çoğu görevi, üçüncü taraf paketleri yüklemeye gerek kalmadan kapatır: URL üzerinden kimlik doğrulama, CONNECT üzerinden tünelleme, HTTP/2 ile uyumluluk ve bağlantı havuzlama hemen çalışır. Proxy'lerin rotasyonu ve sağlık kontrolü, saf promise ve zamanlayıcılar üzerinde kolayca oluşturulabilir ve SOCKS5 gerektiğinde özel bir bağlantı bağlayıcısı ile manuel olarak uygulanabilir; ancak üretim için SOCKS'a özel bir kütüphane almak daha mantıklıdır.

Eğer undici scriptiniz pazaryerlerini analiz etmek veya fiyat izlemek içinse, rezidans proxy'lerine dikkat edin — bunlar, klasik veri merkezi IP'lerine göre anti-bot sistemlerine karşı daha dayanıklıdır ve Wildberries, Ozon veya Avito'ya yapılan toplu isteklere karşı daha az captcha çözümü gerektirir.