모든 브라우저 자동화 개발자가 딱 한 번씩은 빠지는 함정이 있습니다. Chrome의 실행 플래그에 --proxy-server=http://user:pass@host:port를 추가하면 브라우저는 멀쩡히 시작되지만, 이후 모든 페이지가 스크립트로는 볼 수도 클릭할 수도 없는 인증 팝업에서 멈춰버립니다. Chrome은 프록시 플래그에서 자격 증명을 조용히 제거합니다. 오류도, 로그 한 줄도 없이, 그저 아무도 보고 있지 않은 브라우저에 모달 대화상자만 떠 있을 뿐입니다.
이 가이드는 2026년 Selenium과 Playwright에서 실제로 동작하는 프록시 인증 방법의 완전한 지도입니다 — 무엇을 써야 하고, 무엇을 피해야 하며, 각각의 정확한 코드까지. (이러한 도구에서의 프록시 선택 및 회전 전략은 Playwright & Puppeteer에서 프록시 사용하기를 참고하세요 — 이 글은 오직 자격 증명을 통과시키는 것에만 집중합니다.)
명령줄 HTTP 클라이언트는 프록시 URL에서 자격 증명을 파싱해, 프록시의 407 Proxy Authentication Required에 Proxy-Authorization 헤더로 자동 응답합니다. Chromium은 그렇지 않습니다. --proxy-server는 오직 scheme://host:port만 받아들이고, 407이 돌아오면 대화형 대화상자를 띄웁니다. 헤드리스 모드에서는 대화상자조차 없습니다 — 요청이 그냥 실패합니다.
그래서 질문은 결코 "비밀번호를 URL에 어떻게 넣을까"가 아닙니다 — "어떤 계층이 나를 대신해 407에 응답하는가"입니다. 동작하는 답은 네 가지이며, 좋은 순서대로 소개합니다.
Playwright는 이 문제를 제대로 해결했습니다. CDP를 통해 프록시 인증 챌린지에 스스로 응답합니다. 자격 증명은 실행 또는 컨텍스트 옵션에 들어갑니다:
# Python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={
"server": "http://us.jibaoproxy.com:913",
"username": "USERNAME-session-job1",
"password": "PASSWORD",
}
)
// Node.js - and per-context, which is where it gets powerful:
const browser = await chromium.launch({
proxy: { server: 'per-context' } // declare intent at launch
});
const ctx = await browser.newContext({
proxy: {
server: 'http://us.jibaoproxy.com:913',
username: 'USERNAME-session-job2',
password: 'PASSWORD',
}
});
컨텍스트별 프록시는 하나의 브라우저 프로세스가 N개의 서로 다른 sticky 세션 위에서 N개의 컨텍스트를 실행할 수 있다는 뜻입니다 — 모든 본격적인 Playwright 스크래핑 설정의 토대입니다. Playwright를 쓰고 있다면 끝났습니다. 검증 섹션으로 건너뛰세요. 아래의 모든 내용은 Selenium에 이에 상응하는 기능이 없기 때문에 존재합니다.
selenium-wire는 업스트림 인증을 처리하는 로컬 MITM 프록시로 Selenium을 감쌉니다:
from seleniumwire import webdriver # pip install selenium-wire
options = {
"proxy": {
"http": "http://USERNAME:[email protected]:913",
"https": "https://USERNAME:[email protected]:913",
}
}
driver = webdriver.Chrome(seleniumwire_options=options)
driver.get("https://example.com")
프로덕션에 들어가기 전에 알아야 할 주의사항:
고전적인 접근법: 프록시를 설정하고 chrome.webRequest.onAuthRequired를 통해 인증에 응답하는 작은 생성형 확장 프로그램입니다. MITM도, 추가 프로세스도 없이 순수 Selenium에서 동작합니다:
import zipfile, json
HOST, PORT = "us.jibaoproxy.com", 913
USER, PASS = "USERNAME-session-sel1", "PASSWORD"
manifest = {
"version": "1.0.0", "manifest_version": 2, "name": "Proxy Auth",
"permissions": ["proxy", "webRequest", "webRequestBlocking", ""],
"background": {"scripts": ["background.js"]},
}
background = f"""
chrome.proxy.settings.set({{value: {{mode: "fixed_servers", rules: {{
singleProxy: {{scheme: "http", host: "{HOST}", port: {PORT}}}
}}}}, scope: "regular"}}, () => {{}});
chrome.webRequest.onAuthRequired.addListener(
() => ({{authCredentials: {{username: "{USER}", password: "{PASS}"}}}}),
{{urls: [""]}}, ["blocking"]
);
"""
with zipfile.ZipFile("proxy_auth.zip", "w") as zp:
zp.writestr("manifest.json", json.dumps(manifest))
zp.writestr("background.js", background)
from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.add_extension("proxy_auth.zip")
driver = webdriver.Chrome(options=opts)
두 가지 함정: 확장 프로그램은 클래식 헤드리스 모드에서 로드되지 않습니다 — --headless=new(Chrome 109+)를 쓰거나 헤드 모드로 실행하세요. 그리고 Manifest V2 백그라운드 스크립트는 사이드로드된 자동화 확장 프로그램에서는 여전히 동작하지만 Chrome의 MV2 폐기 일정을 주시하세요 — MV3 등가물은 서비스 워커와 동일한 onAuthRequired 리스너가 필요합니다.
자격 증명을 보유하는 작은 로컬 프록시를 실행하고, 브라우저를 인증이 전혀 없는 localhost로 향하게 하세요. 모든 브라우저, 모든 드라이버, 모든 언어에서, 헤드리스든 아니든 동작합니다:
# Using pproxy (pip install pproxy) - terminal 1:
pproxy -l http://127.0.0.1:8899 \
-r "http://USERNAME:[email protected]:913"
# Your script - terminal 2: plain Selenium, no auth needed
opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://127.0.0.1:8899")
driver = webdriver.Chrome(options=opts)
이것은 Firefox/geckodriver(확장 프로그램 트릭이 적용되지 않는 곳)와 Electron 앱 테스트 같은 특이한 환경에 대한 가장 깔끔한 답이기도 합니다. 비용: 감독해야 할 움직이는 부품이 하나 더 늘고, 컨텍스트별 라우팅에는 세션당 포워더 포트가 하나씩 필요합니다.
| 방법 | 동작 환경 | 헤드리스 | 컨텍스트별 세션 | 프로덕션 평가 |
|---|---|---|---|---|
| Playwright 네이티브 | Playwright | 예 | 예 | 기본 선택 |
| selenium-wire | Selenium (Py) | 예 | 드라이버별 | 중간 규모에서 무난 |
| 인증 확장 프로그램 | Selenium (모든 언어) | --headless=new만 | 드라이버별 | 탄탄, MV3 주시 |
| 로컬 포워더 | 전부 | 예 | 세션당 포트 하나 | 만능 폴백 |
실제 IP로의 조용한 폴백은 계정을 차단당하게 만드는 실패 모드입니다. 매 실행 시작 시 출구 IP를 단언(assert)하세요:
ip = driver.execute_script(
"return fetch('https://api.ipify.org').then(r => r.text())"
) if hasattr(driver, 'execute_script') else page.evaluate(
"fetch('https://api.ipify.org').then(r => r.text())"
)
assert ip != MY_REAL_IP, "Proxy not applied - aborting before we leak"
--proxy-server에 자격 증명을 전달했고 Chrome이 그것을 버렸습니다. 네 가지 방법 중 하나를 쓰세요.ERR_PROXY_CONNECTION_FAILED — 호스트/포트가 틀렸거나 프로토콜이 맞지 않습니다. 같은 줄을 먼저 curl -x로 테스트하세요. curl이 동작하면 문제는 브라우저 배선입니다.--headless=new로 전환하세요.브라우저는 user:pass@ 프록시 URL을 읽지 않습니다 — 무언가가 대신 407에 응답해야 합니다. Playwright에서는 그 무언가가 내장되어 있고, Selenium에서는 selenium-wire, 생성형 인증 확장 프로그램, 또는 로컬 포워더입니다. 비교 표에서 자신의 스택에 맞게 고른 다음, 매 실행마다 출구 IP를 단언해 조용한 인증 실패가 크롤링 도중 실제 주소를 절대 유출하지 못하게 하세요.
신규 사용자는 가입 시 500MB를 받고, 첫 충전 시 추가 보너스를 받습니다. 기간 한정 혜택입니다.