Node.js'te Proxy Kullanımı: Axios ve node-fetch

Yayın tarihi:

10 dk okuma

Acar Diveroli
Yazar: Acar Diveroli
Node küpünden axios ve fetch kartlarına, oradan bir proxy düğümüne uzanan bağlantı çizimi

Python'da proxy tanımlamak çoğu kütüphanede tek bir proxies parametresidir. Node.js'te ise durum biraz daha dağınıktır: yerleşik fetch, Axios ve node-fetch proxy'yi üç farklı mekanizmayla alır, SOCKS5 için ayrı bir paket gerekir ve bir kütüphaneye yanlış seçeneği verdiğinizde çoğu zaman hata bile almazsınız; istek sessizce proxy'siz gider. Hangi kütüphanenin hangi seçeneği okuduğunu bilmek bu yüzden önemlidir.

Bu yazıda Node.js'te proxy ayarlamanın üç temel yolunu (ortam değişkeni, agent ve dispatcher), yerleşik fetch, Axios ve node-fetch ile kimlik doğrulamalı proxy kullanımını, SOCKS5 proxy'yi ve IP rotasyonu ile yeniden denemeyi çalışan kodlarla anlatıyoruz. Örnekleri Node.js 24 üzerinde, kullanıcı adı ve şifre isteyen yerel bir test proxy'si üzerinden çalıştırdık; karşılaştığımız sürüm uyumsuzluğunu ve kimlik doğrulama hatalarının kütüphaneden kütüphaneye nasıl farklı göründüğünü de not ettik.

Node.js'te proxy ayarlamanın yolları

Node.js'te bir HTTP isteğini proxy'den geçirmenin üç mekanizması vardır. Hangi kütüphaneyi kullandığınız, hangi mekanizmanın çalışacağını belirler.

  1. Ortam değişkeni. HTTP_PROXY, HTTPS_PROXY ve NO_PROXY değişkenleri. Kod değiştirmeden bütün betiği proxy'den geçirir; ancak her kütüphane bu değişkenleri okumaz.
  2. Agent. Node.js'in klasik http ve https modülleri bağlantıyı bir Agent nesnesi üzerinden kurar. Axios ve node-fetch bu modülleri kullandığı için proxy'ye bağlanan bir ajan (örneğin HttpsProxyAgent) verildiğinde istekler proxy'den geçer.
  3. Dispatcher. Node.js'in yerleşik fetch fonksiyonu klasik http modülünü değil, undici adlı istemciyi kullanır. undici'de bağlantıyı yöneten nesnenin adı dispatcher'dır ve proxy için ProxyAgent verilir.

Bu ayrım yazının en önemli noktasıdır: yerleşik fetch agent seçeneğini tanımaz. Axios alışkanlığıyla fetch(url, { agent }) yazarsanız hata almazsınız, istek proxy'siz gider.

Proxy'nin genel çalışma mantığını ve HTTPS isteklerinde kurulan CONNECT tünelini Proxy Sunucu Nedir ve Nasıl Çalışır? yazımızda anlattık. cURL komutlarını fetch ve Axios'a çevirmek (başlık, gövde, form verisi) ise JavaScript'te cURL yazımızın konusu; bu yazı yalnızca proxy tarafına odaklanır.

Yerleşik fetch ile proxy nasıl kullanılır?

undici ProxyAgent

undici paketini kurun:

bash
npm install undici

Ardından fetch ve ProxyAgentaynı paketten alın:

javascript
import { fetch, ProxyAgent } from "undici";

const dispatcher = new ProxyAgent("http://kullanici:parola@pr.proxynet.io:8000");

const res = await fetch("https://httpbin.org/ip", {
  dispatcher,
  signal: AbortSignal.timeout(20_000),
});
console.log(res.status, await res.json());

Proxy adresi http:// ile başlasa da hedef HTTPS sitelere güvenle gidebilirsiniz: ProxyAgent proxy ile bir CONNECT tüneli kurar ve TLS bağlantısı bu tünelin içinde hedefle yapılır.

Sürüm uyumsuzluğu tuzağı. Node.js kendi içinde bir undici sürümü taşır; npm'den kurduğunuz undici bundan daha yeni olabilir. npm'den gelen ProxyAgent'ı Node.js'in yerleşik fetch fonksiyonuna verdiğimizde (import etmeden global fetch ile) Node.js 24.11 ve undici 8.10 ikilisinde istek fetch failed hatasıyla düştü; hatanın nedeni invalid onRequestStart method idi. Aynı ajanı undici'nin kendi fetch'iyle kullandığımızda sorun olmadı. Kural basit: ProxyAgent'ı hangi paketten aldıysanız fetch'i de oradan alın.

Uygulamadaki bütün undici fetch çağrılarının aynı proxy'yi kullanması için genel bir dispatcher tanımlanabilir:

javascript
import { fetch, ProxyAgent, setGlobalDispatcher } from "undici";

setGlobalDispatcher(new ProxyAgent("http://kullanici:parola@pr.proxynet.io:8000"));

const res = await fetch("https://httpbin.org/ip"); // dispatcher yazmaya gerek yok

NODE_USE_ENV_PROXY ile ortam değişkeni

Güncel Node.js sürümlerinde yerleşik fetch, NODE_USE_ENV_PROXY etkinleştirildiğinde standart proxy ortam değişkenlerini okur. Node.js'in ağ yapılandırma rehberine göre özellik 22.21.0 ve 24.5.0 sürümlerinden itibaren kullanılabilir; aynı davranış --use-env-proxy komut satırı seçeneğiyle de açılır.

Linux ve macOS'ta:

bash
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://kullanici:parola@pr.proxynet.io:8000" node uygulama.mjs

Windows PowerShell'de:

powershell
$env:NODE_USE_ENV_PROXY = "1"
$env:HTTPS_PROXY = "http://kullanici:parola@pr.proxynet.io:8000"
node uygulama.mjs

Bu yöntemde kod tarafında hiçbir değişiklik gerekmez; fetch("https://httpbin.org/ip") doğrudan proxy'den geçer. Testimizde aynı komut NODE_USE_ENV_PROXY olmadan çalıştırıldığında HTTPS_PROXY tanımlı olmasına rağmen istek proxy'ye hiç uğramadı. İç ağ adreslerini NO_PROXY değişkeniyle proxy dışında tutabilirsiniz.

Ortam değişkenlerini kod içinden okuyan bir dispatcher isterseniz undici'nin EnvHttpProxyAgent sınıfı da aynı işi yapar:

javascript
import { fetch, EnvHttpProxyAgent } from "undici";

const res = await fetch("https://httpbin.org/ip", { dispatcher: new EnvHttpProxyAgent() });

Axios ile proxy nasıl kullanılır?

Axios, Node.js'te klasik http ve https modüllerini kullanır ve proxy için iki yol sunar.

Yerleşik proxy seçeneği

Axios'un istek yapılandırmasındaki proxy seçeneği, şifresiz http:// adresler için çalışır:

javascript
import axios from "axios";

const { data } = await axios.get("http://httpbin.org/ip", {
  proxy: {
    protocol: "http",
    host: "pr.proxynet.io",
    port: 8000,
    auth: { username: "kullanici", password: "parola" },
  },
  timeout: 20_000,
});
console.log(data);

Burada auth alanına yazılan şifre kodlanmaz; özel karakterleri olduğu gibi yazabilirsiniz.

HTTPS adresler için https-proxy-agent

Hedef adres HTTPS olduğunda tünelin ajan tarafından kurulması daha güvenilir sonuç verir:

bash
npm install axios https-proxy-agent
javascript
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const httpsAgent = new HttpsProxyAgent("http://kullanici:parola@pr.proxynet.io:8000");

const client = axios.create({
  httpsAgent,
  proxy: false, // Axios'un kendi proxy mantığını kapatır, tüneli ajana bırakır
  timeout: 20_000,
});

const { status, data } = await client.get("https://httpbin.org/ip");
console.log(status, data);

proxy: false satırını atlamayın. Axios, proxy seçeneği verilmediğinde ortam değişkenlerindeki proxy'yi kendi mantığıyla uygulamaya çalışabilir; bu, ajanla çakışan iki farklı proxy davranışı demektir. Ajanı axios.create ile bir kez bağlamak, her çağrıda tekrar yazmanızı önler.

node-fetch ile proxy nasıl kullanılır?

node-fetch, yerleşik fetch gelmeden önce Node.js'te en yaygın fetch uygulamasıydı ve eski projelerde hâlâ sık görülür. Yerleşik fetch'in aksine klasik http modülünü kullandığı için proxy agent seçeneğiyle verilir:

bash
npm install node-fetch https-proxy-agent
javascript
import fetch from "node-fetch";
import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent("http://kullanici:parola@pr.proxynet.io:8000");

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

node-fetch'in 3. sürümü yalnızca ES modülü olarak yayınlanır; require kullanan bir CommonJS projesinde import ile yükleyemiyorsanız yerleşik fetch'e geçmek genellikle daha az zahmetlidir. Yeni bir projede node-fetch eklemek için bir neden yoktur.

SOCKS5 proxy nasıl kullanılır?

undici'nin ProxyAgent'ı HTTP proxy içindir; yerleşik fetch'in SOCKS5 desteği yoktur. SOCKS5 proxy ile çalışmak için socks-proxy-agent paketi Axios ve node-fetch ile birlikte kullanılır:

bash
npm install socks-proxy-agent
javascript
import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";

// socks5h: alan adı proxy tarafında çözülür, DNS sorgusu sizin ağınızdan çıkmaz
const agent = new SocksProxyAgent("socks5h://kullanici:parola@pr.proxynet.io:1080");

const { data } = await axios.get("https://httpbin.org/ip", {
  httpAgent: agent,
  httpsAgent: agent,
  proxy: false,
});
console.log(data);

Aynı ajan node-fetch'te fetch(url, { agent }) biçiminde verilir. Test proxy'mizin kayıtlarında socks5h:// şemasıyla gönderilen isteklerin proxy'ye IP adresi değil alan adı olarak ulaştığını gördük. socks5:// şeması ise alan adını sizin bilgisayarınızda çözer; bunun hangi sorunlara yol açtığını WebRTC ve DNS Sızıntısı yazımızda anlattık. SOCKS5 ile HTTP proxy arasında seçim yapmadan önce SOCKS ve HTTP Proxy Farkı yazımıza bakabilirsiniz.

Kütüphaneler proxy'yi nasıl alır?

İstemciProxy nasıl verilir?Ortam değişkeniSOCKS5Kimlik doğrulama hatası
Yerleşik fetchNODE_USE_ENV_PROXY veya --use-env-proxyYalnızca bayraklaYokfetch failed
undici fetchdispatcher: new ProxyAgent(...)EnvHttpProxyAgent ileYokfetch failed, neden: istek iptal edildi
Axiosproxy seçeneği veya httpsAgent + proxy: falseSeçenek verilmezsesocks-proxy-agent ileDurum kodu 407 olan hata
node-fetchagentYoksocks-proxy-agent ileKullanılan ajana bağlı
http.requestagentYoksocks-proxy-agent ileKullanılan ajana bağlı

Tablonun son sütunu hata ayıklarken önemlidir. Yanlış şifreyle yaptığımız denemede Axios, Request failed with status code 407 mesajı ve error.response.status === 407 değeriyle nedeni açıkça gösterdi. undici'nin fetch'i ise yalnızca fetch failed hatası verdi ve nedeni "Request was cancelled" olarak bildirdi; mesajın kendisi kimlik doğrulamadan söz etmiyor. undici ile fetch failed görüyorsanız ilk kontrol edilecek şey proxy kullanıcı adı ve şifresidir. 407'nin tüm nedenlerini Proxy Kimlik Doğrulama: User:Pass ve IP Whitelist yazımızda topladık.

Kimlik bilgileri ve özel karakterler

Proxy adresini URL olarak veren bütün yöntemlerde (undici ProxyAgent, https-proxy-agent, socks-proxy-agent, ortam değişkenleri) şifredeki @, :, / ve # gibi karakterler kodlanmalıdır. Aksi hâlde adres yanlış ayrıştırılır.

javascript
const user = process.env.PROXY_USER;
const pass = encodeURIComponent(process.env.PROXY_PASS);

const proxyUrl = `http://${user}:${pass}@pr.proxynet.io:8000`;

Bilgileri kodun içine yazmak yerine ortam değişkeninden okumak, kodun paylaşıldığı her yerde şifrenin görünmesini önler. Axios'un proxy.auth alanında ise şifre kodlanmadan yazılır.

IP rotasyonu ve yeniden deneme

Gerçek bir veri toplama işinde istekler zaman zaman başarısız olur: proxy'nin çıkış noktası hedefe ulaşamaz, hedef 429 veya 503 döner, bağlantı zaman aşımına uğrar. Aşağıdaki örnek birden fazla proxy arasından rastgele seçim yapar, başarısız isteği üstel beklemeyle yeniden dener ve aynı anda çalışan istek sayısını küçük gruplarla sınırlar:

javascript
import { fetch, ProxyAgent } from "undici";

const PROXIES = [
  "http://kullanici:parola@pr.proxynet.io:8000",
  "http://kullanici:parola@pr.proxynet.io:8001",
];
const agents = PROXIES.map((p) => new ProxyAgent(p));
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function getWithRetry(url, { attempts = 4, baseMs = 500 } = {}) {
  let lastError;
  for (let i = 0; i < attempts; i++) {
    const dispatcher = agents[Math.floor(Math.random() * agents.length)];
    try {
      const res = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20_000) });
      if (res.status === 429 || res.status >= 500) {
        await res.body?.cancel();
        throw new Error(`HTTP ${res.status}`);
      }
      return await res.text();
    } catch (err) {
      lastError = err;
      await sleep(baseMs * 2 ** i + Math.random() * 250);
    }
  }
  throw lastError;
}

async function crawl(urls, concurrency = 5) {
  const results = [];
  for (let i = 0; i < urls.length; i += concurrency) {
    const batch = urls.slice(i, i + concurrency);
    results.push(...(await Promise.allSettled(batch.map((u) => getWithRetry(u)))));
  }
  return results;
}

const urls = ["https://httpbin.org/ip", "https://httpbin.org/status/503", "https://example.com/"];
const results = await crawl(urls, 2);
results.forEach((r, i) =>
  console.log(urls[i], r.status, r.status === "fulfilled" ? `${r.value.length} bayt` : r.reason.message),
);

Kodun üç ayrıntısı önemlidir:

  • Promise.allSettled, gruptaki bir isteğin başarısız olması diğerlerini durdurmaz. Promise.all kullansaydık tek bir 503 bütün grubun sonucunu kaybettirirdi. Örnekte /status/503 adresi dört denemenin ardından rejected olarak işaretlendi, diğer adresler sonucunu döndürdü.
  • res.body?.cancel(), yeniden deneyeceğimiz yanıtın gövdesini okumadan bağlantıyı serbest bırakır. Okunmayan gövdeler bağlantı havuzunu doldurabilir.
  • Rastgele bileşen (Math.random() * 250), aynı anda başarısız olan isteklerin aynı anda yeniden denenip yeni bir yığılma oluşturmasını önler.

Proxy listesini kendiniz yönetmek yerine tek bir adres üzerinden her bağlantıda farklı çıkış IP'si veren Rotating Proxy kullanırsanız PROXIES dizisi tek elemana iner ve rotasyon sağlayıcı tarafında yapılır. Oturum boyunca aynı IP'nin korunması gereken işlerde (giriş yapılan sayfalar, çok adımlı akışlar) Sticky Proxy tercih edilir.

Bu örnek 429 yanıtında Retry-After başlığını okumaz. Başlığı dikkate alan ve hangi durum kodunda yeniden denenmemesi gerektiğini ayıran bir yaklaşımı Scraping'de HTTP Hata Kodları yazımızda anlattık. Eşzamanlılık değerinin hızı gerçekte nasıl etkilediğini ise Concurrency ve Parallelism yazımızda ele aldık.

Proxy'nin çalıştığı nasıl doğrulanır?

Her yeni kurulumda isteğin gerçekten proxy'den geçtiğini kontrol edin. En kolay yöntem, çıkış IP adresini döndüren bir adrese önce proxy'siz, sonra proxy ile istek atmaktır:

javascript
import { fetch, ProxyAgent } from "undici";

const ip = async (options = {}) => (await (await fetch("https://api.ipify.org?format=json", options)).json()).ip;

console.log("proxy'siz:", await ip());
console.log("proxy ile:", await ip({ dispatcher: new ProxyAgent(process.env.HTTPS_PROXY) }));

İki satır aynı adresi gösteriyorsa istek proxy'den geçmiyordur. Bu durumda en sık neden, yerleşik fetch'e agent verilmesi ya da Axios'ta proxy: false yazılmadan ajan kullanılmasıdır.

Sık yapılan hatalar

  • Yerleşik fetch'e agent vermek. Seçenek sessizce yok sayılır. fetch için doğru anahtar dispatcher'dır.
  • npm undici'nin ProxyAgent'ını global fetch'e vermek. Sürümler uyuşmadığında fetch failed hatası alırsınız; fetch'i de undici'den import edin.
  • HTTPS_PROXY tanımlayıp yerleşik fetch'in onu okuyacağını varsaymak. NODE_USE_ENV_PROXY veya --use-env-proxy olmadan okumaz.
  • Axios'ta ajanla birlikte proxy: false yazmamak. İki proxy mantığı çakışır.
  • Şifredeki özel karakterleri kodlamamak. Adres yanlış ayrıştırılır ve kimlik doğrulama başarısız olur.
  • socks5:// ile DNS'i yerelde çözmek. Uzak çözümleme için socks5h:// kullanın.
  • Zaman aşımı koymamak. Yanıt vermeyen bir proxy çıkışı, zaman aşımı olmayan bir isteği dakikalarca bekletir. undici'de AbortSignal.timeout, Axios'ta timeout kullanın.
  • Bütün isteği Promise.all ile tek seferde başlatmak. Hem kendi bağlantı havuzunuzu hem hedef sitenin hız sınırını zorlar.

Hangi yöntemi seçmeli?

DurumunuzÖneri
Yeni proje, bağımlılık az olsunundici fetch + ProxyAgent
Kodu değiştirmeden mevcut betiği proxy'den geçirmekNODE_USE_ENV_PROXY=1 + HTTPS_PROXY
Projede zaten Axios varAxios + https-proxy-agent + proxy: false
Eski proje, node-fetch kullanıyornode-fetch + agent
SOCKS5 proxyAxios veya node-fetch + socks-proxy-agent (socks5h://)
Her istekte farklı IPRotating proxy, tek adres
Oturum boyunca aynı IPSticky proxy
Hata mesajlarının açık olması önemliAxios (407'yi durum koduyla gösterir)

Tarayıcıda çalışan JavaScript'te ise bunların hiçbiri geçerli değildir: tarayıcıdaki fetch proxy'yi koddan değiştiremez, proxy tarayıcının veya işletim sisteminin ayarında tanımlanır. Windows tarafındaki kurulum için Windows ve Chrome Proxy Ayarları yazımıza bakabilirsiniz.

Sık sorulan sorular

Node.js'in yerleşik fetch'i proxy destekliyor mu?

Node.js 22.21.0 ve 24.5.0'dan itibaren NODE_USE_ENV_PROXY=1 ortam değişkeni veya --use-env-proxy seçeneğiyle HTTP_PROXY ve HTTPS_PROXY değişkenlerini okur. Kod içinden istek bazında proxy vermek için ise undici paketinin fetch ve ProxyAgent'ı kullanılır.

Axios HTTPS_PROXY ortam değişkenini okur mu?

Node.js'te proxy seçeneği verilmediğinde Axios ortam değişkenlerindeki proxy'yi kullanmaya çalışır. Davranışın açık ve öngörülebilir olması için proxy'yi ajanla tanımlayıp proxy: false yazmanızı öneririz.

Her istekte farklı proxy kullanabilir miyim?

Evet. undici'de her fetch çağrısına farklı bir ProxyAgent, Axios'ta farklı bir httpsAgent verebilirsiniz. Ajanları her istekte yeniden oluşturmak yerine yukarıdaki örnekteki gibi bir kez oluşturup yeniden kullanın; her yeni ajan kendi bağlantı havuzunu açar.

Proxy kullanırken çerezler nasıl korunur?

Yerleşik fetch ve undici çerez saklamaz; Set-Cookie başlığını okuyup sonraki isteğe Cookie başlığı olarak eklemeniz gerekir. Axios'ta tough-cookie tabanlı bir çerez kavanozu kullanılabilir. Çerezlerle birlikte IP adresinin de oturum boyunca sabit kalması gerektiğini unutmayın.

Puppeteer veya Playwright'ta proxy nasıl verilir?

Tarayıcı otomasyon araçları proxy'yi Node.js kütüphaneleri gibi değil, tarayıcıyı başlatırken verilen seçenekle alır. Bu yazıdaki ajanlar tarayıcıyı etkilemez. Puppeteer tarafını Puppeteer ve CAPTCHA yazımızda gösterdik.

JavaScript mi Python mu tercih etmeliyim?

Proxy kullanımı iki dilde de mümkündür; Python'da kütüphanelerin çoğu tek bir parametreyle proxy alır, Node.js'te ise kütüphaneye göre mekanizma değişir. Seçimi genellikle ekibin dili ve hedef sayfaların yapısı belirler. Karşılaştırmayı Web Kazıma: JavaScript mi Python mu? yazımızda yaptık; Python kütüphaneleri için HTTPX, Requests ve AIOHTTP yazımıza bakabilirsiniz.

Özetle

Node.js'te proxy, kullandığınız istemciye göre üç farklı yoldan verilir: yerleşik fetch için undici dispatcher veya NODE_USE_ENV_PROXY, Axios ve node-fetch için agent. HTTPS hedeflerde Axios'u https-proxy-agent ve proxy: false ile, SOCKS5'i socks-proxy-agent ve socks5h:// şemasıyla kullanın. ProxyAgent ile fetch'i aynı paketten alın, şifreyi kodlayın, zaman aşımı koyun ve istekleri Promise.allSettled ile küçük gruplar halinde yeniden deneme mantığıyla çalıştırın. Veri toplama işleriniz için uygun paketleri veri kazıma çözümü sayfamızda bulabilirsiniz.

ChatGPT'ye sorClaude'a sor