Puppeteer 프록시 설정: 플래그, 인증, 회전 및 현실
시작 플래그는 쉽습니다; 그 뒤에 오는 407이 대부분의 Puppeteer 프록시 설정을 무너뜨립니다. 여기 전체 패턴이 있습니다 — page.authenticate, 컨텍스트 회전, proxy-chain — 그리고 페이지별 프록시와 헤드리스 탐지에 대한 진실입니다.
Puppeteer 프록시 설정은 하나의 시작 플래그로 시작하고, 대부분의 사람들에게는 한 단계 후에 멈춥니다: 프록시가 407 Proxy Authentication Required를 반환합니다, 왜냐하면 --proxy-server가 자격 증명을 전달할 방법이 없기 때문입니다. 해결책은 내장되어 있습니다 — page.authenticate() — 그러나 그 주위에는 반쯤 작동하는 조언의 지뢰밭이 있습니다: 조용히 트래픽을 Node.js로 우회하는 npm 패키지들, Chromium이 인증하지 않는 SOCKS 스킴, 작동하는 프록시를 죽은 것처럼 보이게 만드는 localhost 우회. 이 가이드는 프로덕션에서 견고한 설정을 안내합니다: 플래그, 인증, 브라우저 컨텍스트로 회전, 난처한 경우를 위한 proxy-chain, 그리고 헤드리스 탐지에 대한 솔직한 섹션.
Puppeteer 프록시 설정: 기본 패턴
프록시는 Chromium 시작 인수이므로 전체 브라우저에 적용됩니다. 자격 증명은 page.authenticate()를 통해 전달되며, 이는 DevTools 프로토콜을 통해 프록시의 407 챌린지에 응답합니다 — 탐색 전에 호출하세요:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
args: ['--proxy-server=http://gate.quantumproxies.io:PORT'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'USER', password: 'PASS' });
await page.goto('https://httpbin.org/ip', { waitUntil: 'domcontentloaded' });
console.log(await page.evaluate(() => document.body.innerText)); // exit IP
await browser.close();
})();
네 가지 세부 사항이 디버깅 시간을 절약합니다:
- 플래그를 템플릿 리터럴이나 연결로 빌드하세요, 일반 따옴표가 아닙니다. 고전적인 Stack Overflow 버그:
"--proxy-server =..."를 이중 따옴표로 감싸고 보간 자리 표시자를 사용하면 변수가 대체되지 않으며, 잘못된 공백이 구문 분석을 깨뜨립니다. - 스킴을 포함하세요. SOCKS의 경우
--proxy-server=socks5://host:port를 사용하세요, 그렇지 않으면 Chromium은 HTTP로 가정합니다. - 플래그에
user:pass@를 포함하지 마세요. Chromium은 이를 무시합니다; 오직page.authenticate()(또는 IP 화이트리스트)만이 인증합니다. - localhost는 프록시를 우회합니다. Chromium은 루프백 URL에 대해 프록시를 건너뜁니다 — Puppeteer 이슈 #3711에 문서화된 퍼즐입니다. 로컬 트래픽을 프록시해야 한다면
--proxy-bypass-list=<-loopback>를 추가하세요; 그렇지 않으면 외부 URL로 테스트하세요.

브라우저 컨텍스트로 프록시 회전
IP당 Chrome을 다시 시작하는 것은 매번 몇 초와 수백 MB의 비용이 듭니다. 브라우저 컨텍스트가 이를 해결합니다: Puppeteer v22부터 API는 browser.createBrowserContext() (이전의 인코그니토 변형을 대체)이며, proxyServer 옵션을 수용하고, 컨텍스트는 자체 쿠키와 저장소를 가진 채 몇 밀리초 만에 생성됩니다:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const jobs = ['https://example.com/a', 'https://example.com/b'];
for (const url of jobs) {
const context = await browser.createBrowserContext({
proxyServer: 'http://gate.quantumproxies.io:PORT',
});
const page = await context.newPage();
await page.authenticate({ username: 'USER', password: 'PASS' });
try {
await page.goto(url, { timeout: 30000 });
// ...extract...
} finally {
await context.close();
}
}
await browser.close();
})();
회전 게이트웨이에 대해, 각 컨텍스트는 자연스럽게 풀의 다른 주소에서 종료됩니다 — 회전 주거용 프록시의 경우 200개 이상의 국가에 걸쳐 90M+ IP가 하나의 호스트 이름 뒤에 있으며, 다중 페이지 흐름이 연속성을 필요로 할 때 고정 세션 사용자 이름 매개변수가 종료를 고정합니다. 이는 Playwright에 대해 권장하는 동일한 작업별 컨텍스트 아키텍처입니다; Puppeteer는 인증 단계를 명시적으로 설명합니다.
회전을 정직하게 유지하는 두 가지 습관이 있습니다. 개발 중에 컨텍스트당 종료 IP를 기록하세요 — 컨텍스트 시작 시 IP-에코 엔드포인트를 타격하고 스크랩된 행과 함께 저장하세요, 그래서 대상이 소프트 블로킹을 시작할 때 하나의 종료 또는 하나의 지문이 원인인지 알 수 있습니다. 그리고 동시성을 제한하세요: 각 컨텍스트는 저렴하지만, 열려 있는 각 페이지는 여전히 렌더러 메모리를 차지하므로, 작업 목록이 수천 개의 URL로 확장될 때 처음으로 컨텍스트 생성을 둘러싼 세마포어가 무제한 루프를 능가합니다.
proxy-chain: 난처한 인증을 위한 로컬 브리지
두 가지 경우가 플래그 플러스 인증 패턴을 깨뜨립니다: 인증된 SOCKS5 (Chromium은 SOCKS 자격 증명 지원이 전혀 없음)와 인증 단계가 없는 순수 프록시 URL만 수용하는 도구들. proxy-chain npm 패키지는 인증된 업스트림으로 포워딩하는 로컬, 자격 증명 없는 프록시를 시작하여 두 가지 모두를 해결합니다:
const puppeteer = require('puppeteer');
const proxyChain = require('proxy-chain');
(async () => {
const upstream = 'http://USER:PASS@gate.quantumproxies.io:PORT';
const localUrl = await proxyChain.anonymizeProxy(upstream);
// localUrl is something like http://127.0.0.1:54321 — no credentials needed
const browser = await puppeteer.launch({
args: ['--proxy-server=' + localUrl],
});
const page = await browser.newPage();
await page.goto('https://httpbin.org/ip');
await browser.close();
await proxyChain.closeAnonymizedProxy(localUrl, true);
})();
중요하게도, 페이지의 요청은 여전히 Chrome 자체에서 출발합니다 — proxy-chain은 바이트만 중계하므로, TLS 지문은 여전히 실제 브라우저의 것입니다. 그 구분이 다음 섹션입니다.
페이지별 프록시: 솔직한 답변
Puppeteer에는 네이티브 페이지별 프록시가 없으며, 인기 있는 해결책 — puppeteer-page-proxy, puppeteer-proxy — 은 모든 요청을 가로채고 HTTP 라이브러리로 Node.js에서 다시 발행한 다음 응답을 브라우저로 되돌려줍니다. 세 가지 결과: 대상은 이제 Chrome 대신 Node TLS 핸드셰이크를 보게 되며, 이는 JA3/JA4 지문 인식이 보호된 사이트에서 즉시 플래그를 설정합니다; 모든 요청은 Node를 통해 왕복 비용을 지불합니다; 그리고 두 패키지는 사실상 유지 관리되지 않으며, 자체 이슈 트래커에 따라 설치가 기본적으로 깨져 있습니다. 다른 페이지에 대해 다른 IP가 필요하다면, 위와 같이 프록시당 하나의 컨텍스트를 사용하세요 — 동일한 효과, 실제 Chrome 트래픽, 지원되는 API.

헤드리스 탐지 현실
프록시는 네트워크 레이어를 고칩니다; 헤드리스 Chrome을 보이지 않게 만들 수는 없습니다. 오래된 헤드리스 모드는 HeadlessChrome User-Agent 토큰으로 자신을 알렸습니다; 새로운 헤드리스 (Puppeteer의 v22 이후 기본값)는 실제 브라우저의 아키텍처를 공유하며 그 간극을 많이 좁혔지만, 탐지기는 여전히 navigator.webdriver, CDP 부작용 및 렌더링 특이점을 탐색합니다 — 스텔스 플러그인은 어제의 검사를 패치할 뿐, 내일의 검사는 아닙니다. 노력 대비 수익률로 수정 사항을 정렬하세요: 깨끗한 주거용 IP가 첫 번째입니다, 왜냐하면 평판은 사이트가 실행하기에 가장 저렴한 필터이며, 코드로는 속일 수 없는 것이기 때문입니다; 합리적인 헤더와 페이싱이 두 번째입니다 (스크래핑 중 CAPTCHA를 피하는 방법에 대한 가이드가 트리거 신호를 다룹니다); 그리고 강화된 대상이 여전히 이길 때, 그 도메인을 Scraper API를 통해 라우팅하여 렌더링, 지문 및 재시도를 처리하고 깨끗한 HTML, markdown 또는 JSON을 반환합니다 — 패치된 브라우저의 함대 대신 하나의 HTTP 호출로.
자주 묻는 질문
Puppeteer에서 프록시를 어떻게 인증합니까?
시작 시 args: ['--proxy-server=http://host:port']로 주소를 설정한 다음, 탐색 전에 모든 페이지에서 await page.authenticate({ username, password })를 호출하세요. 플래그 URL에 포함된 자격 증명은 Chromium에 의해 무시됩니다. 인증이 있는 SOCKS5의 경우, 대신 proxy-chain을 로컬 브리지로 사용하세요, 왜냐하면 Chromium은 SOCKS 자격 증명을 보낼 수 없기 때문입니다.
Puppeteer가 페이지별로 다른 프록시를 사용할 수 있습니까?
네이티브로는 아닙니다 — 시작 플래그는 브라우저 전체에 적용됩니다. 지원되는 등가는 browser.createBrowserContext({ proxyServer })를 통해 프록시당 하나의 브라우저 컨텍스트를 사용하는 것입니다, 각 컨텍스트 내의 페이지와 함께. 진정한 페이지별 프록시를 약속하는 패키지는 요청을 Node.js를 통해 우회하여 TLS 지문을 변경하고 심각한 안티봇 시스템에 의해 플래그가 설정됩니다.
Puppeteer가 SOCKS5 프록시를 지원합니까?
인증되지 않은 엔드포인트에 대해서는 예: --proxy-server=socks5://host:port를 전달하세요. 인증된 SOCKS5는 실패합니다, 왜냐하면 Chromium은 SOCKS 자격 증명 메커니즘이 없고 page.authenticate()는 HTTP 407 챌린지만 응답하기 때문입니다. 해결책: 인증이 있는 게이트웨이의 HTTP 포트를 사용하거나, IP를 화이트리스트에 올리거나, proxy-chain을 로컬 브리지로 실행하세요.
Puppeteer에서 프록시를 어떻게 회전합니까?
작업당 새로운 브라우저 컨텍스트를 createBrowserContext({ proxyServer })로 생성하고 회전 게이트웨이에 지시하세요 — 그러면 각 컨텍스트는 자동으로 새로운 IP에서 종료되며, 관리할 프록시 목록이 필요 없습니다. IP당 전체 브라우저를 다시 시작하는 것도 작동하지만, 회전당 몇 초와 수백 MB의 RAM 비용이 듭니다.
왜 내 Puppeteer 프록시가 localhost에서는 작동하지 않습니까?
Chromium은 설계상 루프백 주소에 대해 프록시를 우회하므로, localhost나 127.0.0.1로의 요청은 직접 전달됩니다 — 충분한 사람들이 놀라서 번호가 매겨진 Puppeteer 이슈가 되었습니다. 프록시를 강제하려면 --proxy-bypass-list=<-loopback>를 추가하거나, 외부 IP-에코 엔드포인트에 대해 프록시를 확인하세요.
견고한 패턴은 작습니다: 주소를 위한 플래그, 자격 증명을 위한 page.authenticate(), 회전을 위한 컨텍스트, 난처한 경우를 위한 proxy-chain — 그리고 요청을 브라우저 밖으로 이동시키는 패키지에 대한 회의론. 그 스택에 깨끗한 주거용 출구를 제공하면 Puppeteer는 지루하게 유지됩니다, 이는 인프라가 받을 수 있는 최고의 칭찬입니다.