Node.js 프록시 설정: Axios, Fetch, Undici 및 회전

axios 프록시 옵션은 HTTPS 대상에서 함정이며, 네이티브 fetch는 프록시 환경 변수를 완전히 무시합니다. 여기 axios, fetch 및 undici에서 작동하는 설정이 있습니다 — 에이전트, 인증, SOCKS5, 회전 및 스트리밍.

Node.js 요청을 프록시를 통해 라우팅하는 것은 간단해 보이지만 디버깅에 오후를 소비하게 됩니다. 그 이유는 대부분의 프로젝트에서 사용하는 세 가지 HTTP 클라이언트 — axios, Node 18에 글로벌로 제공된 네이티브 fetch, 그리고 기본 undici 라이브러리 — 각각 프록시를 다르게 처리하며, 기대한 대로 작동하지 않기 때문입니다. Axios는 문서화된 내장 proxy 구성을 가지고 있으며, Node 전용이며, HTTP 프록시 뒤의 HTTPS 대상에 대해 조용히 깨져 있습니다: 2020년부터 axios GitHub 추적기에 열려 있는 버그이며, 많은 추천을 받은 Stack Overflow 스레드를 유발했습니다. 한편 네이티브 fetch는 HTTP_PROXY 환경 변수를 무시하며 프록시 옵션이 전혀 없습니다. 이 가이드는 각 클라이언트에서 실제로 작동하는 설정, 인증, SOCKS5, 회전 및 스트리밍을 제공합니다.

Axios 프록시 구성 대 프록시 에이전트

Axios는 proxy 객체 — { host, port, auth } — 를 제공하며, 일반 HTTP 대상에 대해서는 잘 작동합니다. 함정은 HTTPS입니다: 대상이 https://이고 프록시가 HTTP를 사용할 때, axios는 CONNECT 터널을 열지 못하고 요청이 멈추거나 실제 IP로 돌아옵니다. 커뮤니티가 정한 해결책은 내장 구성을 완전히 우회하고 대신 axios에 프록시 에이전트를 제공한 다음, 두 메커니즘이 충돌하지 않도록 proxy: false를 설정하는 것입니다:

import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

// Note the destructured import — a default import is the classic gotcha.
const agent = new HttpsProxyAgent("http://USER:PASS@gate.quantumproxies.io:PORT");

const client = axios.create({
  httpAgent: agent,   // for http:// targets
  httpsAgent: agent,  // for https:// targets
  proxy: false,       // disable axios' own broken proxy handling
  timeout: 15000,
});

const r = await client.get("https://httpbin.org/ip");
console.log(r.data); // { origin: "<proxy exit IP>" }

두 가지 세부 사항이 시간을 절약합니다. 첫째, 구조 분해된 { HttpsProxyAgent } 가져오기를 사용하세요 — 최근 버전은 이를 명명된 심볼로 내보내며, 기본 가져오기는 생성 시 예외를 발생시키는 객체를 제공합니다. 둘째, 프록시 URL 스킴은 프록시가 HTTPS 트래픽을 전달할 때에도 http://로 유지됩니다: 스킴은 프록시에 도달하는 방법을 설명하며, 대상에 대한 TLS는 터널 내부에서 실행됩니다. 동일한 실패 클래스가 Python에서도 나타납니다; requests에서도 작업하는 경우, 패턴은 우리의 ProxyError 및 SSLError 수정 가이드와 유사합니다.

인증 및 환경 변수

인증된 프록시는 HTTP 기본 인증을 사용합니다. 에이전트와 함께, URL에 http://user:pass@host:port로 포함하세요; 비밀번호에 @, : 또는 /가 포함되어 있으면 먼저 encodeURIComponent()로 URL 인코딩하세요, 그렇지 않으면 URL이 잘못된 위치에서 분할됩니다. 407 Proxy Authentication Required는 자격 증명이 거부되었거나 소스 IP가 허용 목록에 포함되지 않았음을 의미합니다. Axios는 또한 HTTP_PROXY, HTTPS_PROXY, NO_PROXY를 환경에서 읽습니다 — 코드에 손대지 않고 타사 라이브러리를 프록시하는 데 유용합니다 — 그리고 proxy: false를 설정하여 이를 비활성화합니다. 중요한 경고: 네이티브 fetch는 이러한 변수를 읽지 않습니다, 따라서 env-var 프록시를 사용하는 스크래퍼는 axios에서 fetch로 전환하는 순간 조용히 직접 연결됩니다.

Node.js에서 axios, 네이티브 fetch 및 undici가 각각 프록시를 수용하는 방법을 비교하여 프록시 에이전트와 undici 디스패처를 보여줍니다.
동일한 요청, 세 가지 배관 시스템. Axios는 에이전트를 원하고, 네이티브 fetch는 undici 디스패처를 원하며, 이들을 혼합하는 것이 '프록시 작동 안 함'의 가장 큰 원인입니다.

undici로 네이티브 fetch 프록시하기

Node의 글로벌 fetch는 undici를 기반으로 하며, 프록시는 undici에 있습니다. ProxyAgent를 생성하고 이를 비표준 dispatcher 옵션으로 전달하세요 — 이것이 fetch를 프록시하는 현대적이고 종속성이 적은 방법이며, HTTPS CONNECT를 상자 밖에서 올바르게 처리합니다:

import { ProxyAgent } from "undici";

const dispatcher = new ProxyAgent({
  uri: "http://gate.quantumproxies.io:PORT",
  token: "Basic " + Buffer.from("USER:PASS").toString("base64"),
});

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

글로벌 fetch가 아닌 undici를 직접 사용하는 경우, 동일한 ProxyAgentrequest() 또는 setGlobalDispatcher()에 연결하여 프로세스 내 모든 fetch를 한 번에 프록시할 수 있습니다. 이 단일 호출 글로벌 스위치는 모든 코드베이스를 프록시를 통해 라우팅하는 가장 깔끔한 방법입니다.

Node.js의 SOCKS5 프록시

axios나 undici는 SOCKS를 기본적으로 지원하지 않습니다 — axios proxy 구성에 socks5:// 문자열을 전달하면 protocol mismatch 예외가 발생합니다. socks-proxy-agent를 설치하고 이를 HTTPS 에이전트와 동일한 방식으로 사용하세요, httpAgenthttpsAgent 모두에 연결합니다. socks5://보다 socks5h://를 선호하세요: 뒤에 오는 h는 프록시 측에서 DNS를 해결하여 DNS 누출을 방지하고 출구 위치에서 지리적으로 제한된 호스트 이름을 해결합니다. 모든 SOCKS5 프록시 계획은 HTTP와 SOCKS5를 통해 동일한 게이트웨이를 노출하므로, 이는 새로운 구매가 아닌 스킴 전환입니다:

import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";

const agent = new SocksProxyAgent("socks5h://USER:PASS@gate.quantumproxies.io:PORT");

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

프록시 목록 없이 회전

오래된 방법 — IP 배열, 요청당 Math.random(), 죽은 IP 제거 — 는 더 이상 유지하지 않는 코드입니다. 회전 게이트웨이는 서버 측에서 매 요청마다 새로운 출구 IP를 할당하므로 하나의 엔드포인트가 전체 풀처럼 동작합니다. 회전 주거 프록시를 통해 그 풀은 90M+ IP를 200+ 국가에 걸쳐 포함하며, 아래 루프는 회전 로직 없이 각 반복마다 다른 출처를 출력합니다:

import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent("http://USER:PASS@gate.quantumproxies.io:PORT");
const client = axios.create({ httpAgent: agent, httpsAgent: agent, proxy: false });

for (let i = 0; i < 3; i++) {
  const r = await client.get("https://httpbin.org/ip");
  console.log(r.data.origin); // a different exit IP each time
}

흐름이 여러 요청 — 로그인, 장바구니에 추가, 결제 — 에 걸쳐 있을 때, 요청당 회전은 세션을 깨뜨립니다. 프록시 사용자 이름의 고정 세션 매개변수는 설정된 시간 동안 하나의 출구 IP를 고정한 다음 회전합니다; 동일한 엔드포인트, 문자열 변경 하나. 이를 수천 개의 동시 요청으로 확장하려면, 작업을 axios 루프에서 벗어나게 하고 대규모 스크래핑 아키텍처에 대한 가이드를 읽어 대기열 및 동시성 예산을 확인하세요.

프록시를 통한 스트리밍 응답

파일이나 큰 JSON 페이로드를 다운로드하는 것은 에이전트가 연결된 후 모든 요청과 동일하게 작동합니다 — responseType: "stream"을 설정하고 본문을 디스크로 파이프합니다. 프록시는 전송을 투명하게 처리하므로 200MB 내보내기가 메모리에 버퍼링되지 않습니다:

import fs from "node:fs";

const r = await client.get("https://example.com/large.json", {
  responseType: "stream",
});
r.data.pipe(fs.createWriteStream("out.json"));
작동하는 Node.js 프록시 구성과 axios, fetch 및 SOCKS5 설정을 망치는 일반적인 실수를 대조하는 체크리스트
대부분의 Node.js 프록시 실패는 다섯 가지 실수로 귀결됩니다: axios HTTPS 버그, 기본 에이전트 가져오기, 중복된 프록시 구성, 잘못된 SOCKS 스킴, 또는 fetch가 환경 변수를 읽을 것으로 기대하는 것.

자주 묻는 질문

왜 내 axios 프록시가 작동하지 않나요?

가장 일반적인 원인은 HTTP 프록시 뒤의 HTTPS 대상입니다: axios의 내장 proxy 구성은 CONNECT 터널을 열지 못하고 실제 IP를 반환하거나 멈춥니다. 프록시 에이전트로 전환하세요 — HttpsProxyAgenthttpAgenthttpsAgent에 연결하고, axios가 자체적으로 프록시를 처리하려고 하는 것을 중지하도록 proxy: false를 설정하세요.

axios는 SOCKS5 프록시를 지원하나요?

기본적으로는 아닙니다 — socks5:// URL을 proxy 구성에 전달하면 프로토콜 불일치 오류가 발생합니다. socks-proxy-agent를 설치하고 SocksProxyAgent를 빌드하여 httpAgenthttpsAgent에 연결하세요. socks5h:// 스킴을 사용하여 DNS가 프록시 측에서 해결되도록 하여 기기에서 누출되지 않도록 하세요.

Node.js에서 네이티브 fetch와 함께 프록시를 어떻게 사용하나요?

네이티브 fetch는 프록시 옵션이 없으며 HTTP_PROXY 환경 변수를 무시합니다. undici ProxyAgent를 생성하고 fetch 호출에서 dispatcher 옵션으로 전달하거나, setGlobalDispatcher()를 한 번 호출하여 프로세스 내 모든 fetch를 프록시하세요. Undici는 추가 구성 없이 HTTPS CONNECT를 올바르게 처리합니다.

환경 변수로 axios 프록시를 어떻게 설정하나요?

전체 프록시 URL로 HTTP_PROXY, HTTPS_PROXY 및 선택적으로 NO_PROXY를 내보내세요; axios는 이를 자동으로 읽습니다. 요청이나 인스턴스에서 proxy: false를 설정하여 axios가 환경을 무시하도록 하세요. 이는 axios에만 영향을 미친다는 점을 기억하세요 — undici 및 네이티브 fetch는 이러한 변수를 인식하지 않습니다.

전체 이야기는 카드에 맞습니다: axios는 에이전트와 proxy: false가 필요하고, 네이티브 fetch는 undici 디스패처가 필요하며, SOCKS는 자체 에이전트가 필요하고, 회전은 루프가 아닌 게이트웨이에 속합니다. 배관을 올바르게 설정하면 마지막 변수는 IP 품질입니다 — 깨끗한 주거 출구는 동일한 코드에서 403을 받는 플래그된 데이터센터 IP를 통과합니다.

첫 번째 요청에서 통과하는 주거 프록시를 얻으세요