Camoufox를 레지덴셜 프록시와 함께 쓰려면 Camoufox() 또는 AsyncCamoufox()에 Playwright 형식의 proxy dict를 넘기고, geoip=True로 출구 IP에서 타임존·로케일·위치 정보를 읽어 오게 한 다음, 스티키 세션을 써서 Camoufox가 실행 시점에 조회한 IP로 실제 브라우징까지 하도록 만들면 됩니다. Camoufox는 JavaScript 오버라이드 묶음이 아니라 핑거프린트 패치를 C++에 넣은 Firefox 빌드입니다. 그래서 사용자에게 남는 일은 네트워크 정체성과 브라우저 정체성을 일관되게 유지하는 것입니다. 이 가이드의 나머지는 코드, 그 코드 뒤의 검증 항목, 그리고 Camoufox가 맞지 않는 경우에 대한 이야기입니다.
Camoufox란 무엇이며 왜 레지덴셜 프록시와 함께 쓰는가
Camoufox는 스크래핑과 AI 에이전트용으로 만든 오픈소스 Firefox 포크입니다. Python에서는 일반 Playwright API(PyPI의 camoufox 패키지)로, 2026년 9월부터는 공식 npm 런처로도 제어할 수 있습니다. README에 따르면 데이터는 "C++ 구현 레벨에서 가로채기" 때문에, navigator, 화면, WebGL, 폰트를 검사하는 페이지는 패치된 JavaScript 함수가 아니라 네이티브 getter를 보게 됩니다.
이는 Chromium 계열 도구와는 다른 접근입니다. Patchright는 Chrome 자체는 건드리지 않고, 드라이버가 Chrome DevTools Protocol(CDP)로 Runtime.enable 호출처럼 탐지 가능한 동작을 하지 않도록 패치한 Playwright 드라이버입니다. nodriver는 WebDriver를 아예 버리고 순정 Chrome과 CDP로 직접 통신합니다. 둘 다 자동화된 Chrome을 Chrome처럼 보이게 합니다. 반면 Camoufox는 자동화된 Firefox를 다양한 Firefox 설치본처럼 보이게 하고, Playwright는 CDP가 아닌 Firefox 고유의 자동화 프로토콜 Juggler로 이를 제어합니다. Camoufox 패치는 Juggler에 격리된 페이지 사본을 제공하므로, Playwright의 헬퍼(예: window.__playwright__binding__)는 페이지 스크립트에 절대 노출되지 않습니다.
레지덴셜 프록시가 필요한 이유는 Camoufox가 IP는 숨길 수 없기 때문입니다. 실행할 때마다 OS, 화면, GPU, 폰트, 음성, 언어로 이루어진 새 기기 정체성이 생성됩니다. 그런데 이 정체성은 "독일의 가정용 데스크톱"이라고 말하는데 연결은 버지니아의 데이터센터 대역에서 온다면, 핑거프린트 작업은 아무 의미가 없습니다. README도 Camoufox가 "로테이팅 프록시(가급적 레지덴셜 IP)와 함께 사용하도록 설계되었다"고 밝힙니다.
Camoufox 설치와 브라우저 다운로드
설치는 두 단계입니다. Python 패키지를 설치한 뒤, 별도로 내려받는 패치된 Firefox 바이너리를 가져옵니다.
pip install -U "camoufox[geoip]"
python -m camoufox fetch
[geoip] extra는 geoip2 리더를 추가하고, Camoufox는 이와 함께 쓸 위치 정보 데이터셋을 내려받습니다. 설치 문서는 이를 선택 사항이라고 하면서도 "프록시를 사용한다면 강력히 권장"하며, 이것 없이는 geoip=True가 동작하지 않습니다. 의존성을 고정할 때 중요한 버전 관련 사항이 두 가지 있습니다.
- 2026년 10월 초 기준 PyPI의 최신 안정 릴리스는 Firefox 152 기반의
camoufox 0.5.6(2026년 9월 6일)입니다. 9월 말에는 Firefox 156 기반 빌드가 GitHub 프리릴리스와0.5.7b*베타로 배포되고 있었습니다. 특정 수정이 필요한 게 아니라면 안정 버전을 쓰세요. - 이 패키지는 Python 3.10 이상이 필요하고, 서버 모드가 Playwright의 비공개 모듈을 import하기 때문에
pyproject.toml에서playwright<1.63으로 고정되어 있습니다. 프로젝트의 다른 부분이 최신 Playwright를 요구한다면 충돌을 예상해야 합니다.
Python API: sync, async, 그리고 중요한 옵션
Camoufox는 Playwright의 Firefox 런처를 감싸고 있으므로, 브라우저 객체를 얻은 뒤에는 모든 Playwright 호출이 평소처럼 동작합니다. sync와 async 진입점은 컨텍스트 매니저입니다.
from camoufox.sync_api import Camoufox
with Camoufox(headless=True) as browser:
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
import asyncio
from camoufox.async_api import AsyncCamoufox
async def main():
async with AsyncCamoufox(headless=True) as browser:
page = await browser.new_page()
await page.goto("https://example.com")
print(await page.title())
asyncio.run(main())
프록시를 쓰는 작업에서 특히 중요한 키워드 인자는 다음과 같습니다. 설명은 사용법 문서와 pythonlib 소스의 launch_options() docstring을 참고했습니다.
proxy: Playwright 프록시 dict(server,username,password).geoip=True이면 Camoufox는 실행 시 출구 IP를 알아내기 위해 이 프록시로 요청을 하나 더 보냅니다.geoip: 출구 IP를 자동 조회하려면True, 아니면 IP 문자열을 직접 지정합니다. 이 IP를 바탕으로 Camoufox가 위도·경도, 타임존, 국가, 로케일, WebRTC IP를 설정합니다. 데이터베이스는geoip_db로 고릅니다. 2026년 9월 30일 main 브랜치에서 "GeoIP AIO"가 기본값이 되고 GeoLite2는 deprecated되었으니, 설치한 버전이 어느 쪽을 쓰는지 확인하세요.locale:"en-US"같은 명시적 로케일,"US"같은 국가 코드(이 경우 Camoufox가 해당 국가의 사용 언어 분포에서 언어를 고릅니다), 또는 리스트를 받습니다. 첫 번째 항목이IntlAPI를 결정합니다.humanize:True또는 커서 이동 최대 시간(초)을 나타내는 float. 안정 버전 0.5.6은 여전히 베지어 곡선을 그립니다. main 브랜치는 2026년 9월 24일에 Cursory 데이터셋에서 녹화한 실제 사람의 마우스 경로를 재생하는 방식으로 바뀌었으므로, 이 기능은 Firefox 156 프리릴리스 이후 빌드에서만 쓸 수 있습니다.headless: 기본값은False.True는 Firefox headless를 쓰며, Camoufox가 일반 창처럼 보이도록 패치합니다."virtual"은 Xvfb 가상 디스플레이 안에서 브라우저를 headful로 실행합니다. 이 옵션은 Linux 전용이며xvfb패키지가 필요합니다.os: 생성되는 정체성을"windows","macos","linux"또는 이들의 리스트로 제한합니다.block_webrtc,block_images,block_webgl: on/off 옵션입니다. 프로젝트 자체 경고에 따르면 WebGL이나 이미지를 차단하면 주요 WAF에서 탐지된다는 보고가 있습니다.
가상 디스플레이 문서는 "virtual"을 쓰는 이유를 이렇게 설명합니다. Camoufox가 headless 모드를 패치하긴 했지만 "headless 모드로 실행하는 것은 앞으로도 탐지될 수 있다"는 것입니다. Linux 서버에서 Xvfb는 메모리를 약간 더 쓰는 대신 headless 특유의 흔적 한 부류를 통째로 없애 줍니다. 대개는 그만한 가치가 있습니다.
실전 예제: 국가 타기팅, 스티키 세션, 일관성
아래 예제는 ProxyHat 게이트웨이로 독일 출구를 쓰는 다단계 세션을 열고, Camoufox가 출구 IP에서 타임존과 로케일을 도출하게 한 뒤, 실제 작업 전에 정체성이 일관적인지 확인합니다. 국가와 세션은 모두 프록시 사용자 이름에 들어갑니다.
import asyncio
import secrets
from camoufox.async_api import AsyncCamoufox
PROXY_USER = "USERNAME"
PROXY_PASS = "PASSWORD"
def proxy_for(country: str, session_id: str) -> dict:
# 스티키 세션 + 국가 타기팅, 둘 다 사용자 이름에 인코딩
return {
"server": "http://gate.proxyhat.com:8080",
"username": f"{PROXY_USER}-country-{country}-sid-{session_id}-ttl-10m",
"password": PROXY_PASS,
}
COHERENCE_JS = """() => ({
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
offsetMin: new Date().getTimezoneOffset(),
lang: navigator.language,
langs: navigator.languages,
platform: navigator.platform,
})"""
async def run(country: str = "DE") -> None:
session_id = secrets.token_hex(4) # 전체 흐름에 IP 하나
async with AsyncCamoufox(
proxy=proxy_for(country, session_id),
geoip=True, # 출구 IP 기준 타임존/로케일/위치/WebRTC
humanize=True, # 사람 같은 커서 이동
headless="virtual", # Linux + Xvfb, 그 외 환경은 headless=True
os=["windows", "macos"],
) as browser:
page = await browser.new_page()
# 1. 외부에서는 무엇이 보이는가?
await page.goto("https://ipinfo.io/json")
exit_info = await page.evaluate("() => JSON.parse(document.body.innerText)")
# 2. 브라우저는 무엇이라고 주장하는가?
claimed = await page.evaluate(COHERENCE_JS)
print("exit:", exit_info.get("ip"), exit_info.get("country"), exit_info.get("timezone"))
print("browser:", claimed)
if exit_info.get("timezone") != claimed["tz"]:
raise RuntimeError("timezone does not match exit IP; abort before real traffic")
# 3. 같은 IP에서 실제 다단계 작업
await page.goto("https://example.com/", wait_until="domcontentloaded")
# ... 로그인, 페이지 넘김, 제출 모두 같은 session_id로
asyncio.run(run("DE"))
이렇게 구성한 이유는 다음과 같습니다.
geoip=True를 쓴다면 스티키 세션은 필수입니다. 조회는 브라우저가 실행될 때 한 번만 일어납니다. Camoufox의public_ip()가 프록시를 거쳐 공개 IP 확인 서비스에 요청을 보내고, 그 응답으로 정체성을 만듭니다. 사용자 이름에 세션이 없으면 로테이팅 게이트웨이가 다음 연결에 다른 레지덴셜 IP를 줄 수 있습니다. 타임존이 하나뿐인 국가라면 거의 드러나지 않지만, 미국·러시아·브라질·호주에서는 두 번째 IP가 브라우저가 보고하는 타임존과 몇 시간씩 차이 날 수 있습니다. 스티키 세션을 쓰면 조회 IP와 브라우징 IP가 같아집니다(자세한 내용은 스티키 vs 로테이팅 세션 가이드 참고).- 세션 ID는 페이지가 아니라 브라우저당 하나입니다. 로테이션은 브라우저를 닫고 새
session_id로 새 브라우저를 띄우는 방식으로 하세요. 그러면 새 IP와 새 핑거프린트가 함께 바뀌는데, 이것이 바로 다른 방문자의 모습입니다. - ipinfo 확인은 비용이 적은 경보 장치입니다. IP 위치 조회 엔드포인트라면 무엇이든 괜찮습니다. 핵심은 대상 사이트에 요청을 쓰기 전에 네트워크가 말하는 것과 브라우저가 말하는 것을 비교하는 것입니다. 위치 정보 데이터베이스는 경계 지역에서 서로 다른 답을 내기도 하므로, 불일치는 고장의 증거라기보다 재실행할 이유로 받아들이세요.
- HTTP 엔드포인트를 우선 쓰세요. Playwright의 네트워크 문서는 사용자 이름/비밀번호 인증을 HTTP(S) 프록시에 대해서만 문서화합니다. SOCKS5(
gate.proxyhat.com:1080)도 프록시 유형으로 지원되지만, 브라우저를 통한 인증 SOCKS는 설정이 자주 깨지는 지점입니다. 더 넓은 장단점은 HTTP vs SOCKS5에서 다룹니다.
핑거프린트 일관성 체크리스트
일관성이란 출구 IP 국가, 타임존, 언어, WebRTC 주소, OS, 폰트, GPU가 모두 한 장소에 있는 그럴듯한 기기 하나를 묘사한다는 뜻입니다. 브라우저 쪽은 Camoufox가 처리하고, 실패는 대부분 사용자가 덮어쓴 설정에서 나옵니다. 규모를 키우기 전에 다음을 확인하세요.
- 출구 IP 국가 ↔ 타임존.
Intl.DateTimeFormat().resolvedOptions().timeZone과Date.getTimezoneOffset()을 모두 확인하세요. 탐지 스크립트는 IP로 추정한 타임존과 브라우저 타임존을 비교하며, 이 불일치는 가장 오래된 프록시 신호 중 하나입니다. 스티키 세션에서geoip=True를 쓰면 구조적으로 일치합니다. - 출구 IP 국가 ↔ 언어.
navigator.language,navigator.languages,Accept-Language헤더가 국가와 맞아야 합니다. 헤더와navigator의 동기화는 Camoufox가 알아서 합니다.locale=을 지정한다면 지역까지 포함한 전체 로케일("de"가 아니라"de-DE")을 넘기세요. 지역이 빠지면 자동 생성되는데, 프로젝트는 이것이 "IP가 로케일 지역과 맞지 않으면 의심을 살 수 있다"고 경고합니다. - WebRTC ↔ 출구 IP. WebRTC는 프록시를 우회해 실제 로컬 주소나 공인 주소를 노출할 수 있습니다.
geoip를 켜면 Camoufox는 프로토콜 레벨에서 WebRTC IPv4를 출구 IP로 위장하고 IPv6 DNS를 끕니다. WebRTC가 아예 없는 데스크톱 Firefox는 드물기 때문에, 차단하지 말고 활성화된 상태로 위장하세요. - Geolocation API ↔ IP. Camoufox는 GeoIP 조회 결과로 위도와 경도를 채우고 권한 pref를 설정하므로,
navigator.permissions.query({name: "geolocation"})는 제공하는 위치와 일관되게 "granted"를 보고합니다.config에서 위치 정보를 직접 지정하지 마세요. 라이브러리는 이것이 "대상 위치가 IP와 맞지 않으면 탐지로 이어질 수 있다"고 경고합니다. - OS ↔ 폰트 ↔ GPU. 생성기는 실제 트래픽의 통계 모델에서 값을 뽑고, 일관성 검사가 Windows User-Agent에 Apple M1 GPU 같은 불가능한 조합을 걸러냅니다.
custom_fonts_only=True는 피하고,webgl_config에는 해당 OS에서 모델이 본 적 있는 조합만 넘기세요(그 외에는ValueError가 발생합니다). - User-Agent: 건드리지 마세요.
User-Agent나navigator필드를 수동으로 지정하는 것은 네트워크와 JavaScript가 서로 다른 말을 하게 만드는 가장 빠른 길입니다. 라이브러리도 둘 다 하지 말라고 경고합니다. - IP 유형 ↔ 주장하는 기기. 호스팅 사업자 ASN 위에서 생성된 일반 소비자 데스크톱은 핑거프린트가 아무리 좋아도 앞뒤가 맞지 않는 조합입니다. 레지덴셜(또는 ISP) IP가 비용값을 하는 지점이 바로 여기입니다. 서버 측에서 이 신호들을 어떻게 조합하는지는 브라우저 핑거프린팅 해설에서 다룹니다.
- 세션당 정체성 하나. 쿠키, IP, 핑거프린트는 모두 함께 바뀌거나 아예 바뀌지 않아야 합니다. 새 IP와 새 GPU에 기존 쿠키가 따라오는 모습은 안정된 세션이나 깨끗한 새 세션보다 훨씬 사람답지 않습니다.
Camoufox vs Patchright vs nodriver: 무엇을 고를까
먼저 엔진으로 고르세요. 대상 사이트가 주로 Chrome 트래픽을 받거나 V8 동작을 검사한다면 Chromium 계열 도구를 쓰세요. JavaScript 패치를 직접 작성하지 않고 깊은 수준의 핑거프린트 로테이션이 필요하며 Firefox 트래픽이어도 괜찮다면 Camoufox를 쓰세요. 아래 표는 2026년 10월 기준 장단점을 요약한 것입니다.
| Camoufox | Patchright | nodriver | |
|---|---|---|---|
| 엔진 | 패치된 Firefox(Gecko + SpiderMonkey) | Chromium / Chrome(브라우저는 패치하지 않음) | 설치된 Chrome, Chromium, Edge 또는 Brave |
| 제어 프로토콜 | Juggler(패치됨, 페이지와 격리) | CDP, isolated world로 Runtime.enable 회피 | CDP 직접 사용, WebDriver 없음 |
| API | Playwright(Python, JS/TS) | Playwright 대체(drop-in)(Python, Node, 커뮤니티 .NET) | 자체 async Python API |
| TLS 핑거프린트 출처 | Firefox 자체 NSS 스택, 즉 Firefox ClientHello | 실제 Chrome의 BoringSSL, 특히 channel="chrome" 사용 시 | 실제 Chrome의 BoringSSL |
| 핑거프린트 로테이션 | 내장, 실행할 때마다 C++ 레벨에서 생성 | 없음. 인젝션 없는 실제 Chrome 사용을 권장 | 내장 기능 없음 |
| 유지보수(2026년 10월) | 다시 활발함. 2026년 9월 안정 버전 0.5.6, 단 2025년 3월~2026년 1월에는 안정 릴리스 없음 | Playwright를 따라감. 2026년 9월 v1.63.0 드라이버 릴리스 | 마지막 PyPI 릴리스 0.50.3, 2026년 5월 |
| 대표적인 실패 유형 | Firefox 특화 검사, Chrome만 통과하는 엔진 검사, 벤더가 찾아내 검사 항목에 추가하는 핑거프린트 불일치 | Console API 비활성화, Chromium 전용, 일반적인 headless·행동 신호는 여전히 적용 | 메인 월드 evaluate가 관찰 가능, 핑거프린트 관리 없음, Playwright보다 좁은 API |
TLS 행은 보기보다 중요합니다. 세 도구 모두 실제 브라우저의 네트워크 스택을 쓰므로, Python requests가 Chrome인 척할 때 생기는 불일치는 없습니다. 하지만 Camoufox는 항상 Firefox 핸드셰이크를 보냅니다. 사이트의 모델이 트래픽 대부분에서 Chrome 형태의 TLS를 기대한다면, JA3/JA4가 이런 프로파일링에 어떻게 쓰이는지 TLS 핑거프린팅 해설을 참고하세요.
솔직한 한계
Camoufox는 JavaScript 패치 없이 일관되고 로테이션되는 Firefox 정체성을 만드는 한 가지 일을 잘하며, 그에 따른 실제 비용도 있습니다.
Firefox는 트래픽에서 작은 비중입니다
StatCounter에 따르면 2026년 9월 전 세계 데스크톱 브라우징에서 Firefox 점유율은 약 6.1%, Chrome은 약 65%입니다. 작은 집단은 모델링하기 쉽고, 그 안의 이상 징후는 더 눈에 띕니다. 어떤 사이트는 Chromium이 아닌 브라우저에 챌린지를 더 쉽게 겁니다. 대상 사이트가 그런지는 직접 트래픽을 보내 테스트하기 전까지는 알 수 없습니다.
Chrome으로 위장할 수 없습니다
README가 직접 말합니다. Camoufox는 "Chromium 핑거프린트 주입을 완전히 지원하지 않으며", 일부 WAF는 "위장이 불가능한 SpiderMonkey 엔진 동작을 검사한다"고요. 사이트가 Chrome만 고집한다면 어떤 Camoufox 설정으로도 통과할 수 없습니다.
유지보수가 고르지 않았습니다
릴리스 이력에 공백이 드러납니다. 중단 전 마지막 안정 빌드는 2025년 3월 15일의 v135.0.1-beta.24였습니다. 원작자가 작업을 멈췄고, 간간이 들어온 커뮤니티 커밋을 제외하면 2026년 1월 Firefox 146 베타가 나올 때까지 아무것도 출시되지 않았습니다. 그 후 README에 Clover Labs가 공식 메인테이너로 명시되었습니다. 이후 프로젝트는 빠르게 움직였습니다. 5월에 Firefox 150 빌드, 7월부터 Firefox 152 안정 빌드, 9월에 공식 TypeScript 런처, 그리고 9월 말 Firefox 156 프리릴리스가 나왔습니다. README에는 여전히 "이 프로젝트는 개발 중입니다. 안정적인 프로덕션 사용에는 적합하지 않을 수 있습니다"라는 경고가 붙어 있습니다. 버전을 고정하고, 업그레이드는 배포 전에 테스트하세요.
핑거프린트는 움직이는 표적입니다
프로젝트는 안티봇 벤더들이 "단 하나의 고유한 불일치라도 찾기 위해 Camoufox를 반복해서 테스트"하고 찾으면 검사 항목을 추가한다고 공개적으로 말합니다. 사람처럼 움직이는 커서도 "충분히 정교한 분석으로는 여전히 탐지될 수 있다"고 합니다. C++ 레벨 위장은 흔적 한 부류를 통째로 없애 주지만, 영원히 탐지되지 않게 해 주지는 않습니다.
리소스 비용
프로젝트는 군더더기를 걷어낸 빌드가 약 200MB로 동작하며 순정 Firefox보다 메모리를 덜 쓴다고 주장합니다. 이는 우리가 아닌 프로젝트의 수치이고, 페이지를 열면 그만큼 더 늘어납니다. 그래도 정체성마다 완전한 브라우저 하나입니다. 핑거프린트를 공유하는 여러 컨텍스트가 아니라 동시 세션마다 브라우저 하나를 계획하고, JavaScript가 필요 없는 엔드포인트에는 일반 HTTP 클라이언트를 쓰세요.
Camoufox가 해결하지 못하는 것
- IP 평판. 플래그가 붙은 레지덴셜 IP는 여전히 플래그가 붙은 상태입니다. 사용 전에 출구를 테스트하세요. 예를 들어 무료 프록시 체커를 쓸 수 있습니다.
- 속도 제한과 행동 패턴. 1분에 수백 번 똑같은 경로를 밟으면 핑거프린트와 상관없이 자동화로 보입니다.
- CAPTCHA, 로그인, 접근 규칙. Camoufox가 무언가를 수집할 권한을 주지는 않습니다. robots.txt와 사이트 약관을 존중하고, 권한 없는 인증 뒤의 데이터는 건드리지 말며, 공개적으로 보이는 개인정보라도 GDPR/CCPA 규제 대상으로 취급하세요.
합리적인 도입 순서
국가 하나, 스티키 세션 하나로 시작하세요. 실행할 때마다 일관성 검사 결과를 기록하고, 한 엔진에 정착하기 전에 같은 대상에서 Patchright를 기준선으로 차단율을 비교하세요. Camoufox가 낫다면 한 브라우저를 여러 세션이 공유하는 대신 새 세션 ID를 가진 브라우저를 추가하는 방식으로 확장하세요. Chrome을 확실히 선호하는 사이트에서 진다면, 위 표가 다음 선택지를 알려 줍니다. 레지덴셜 게이트웨이는 세 도구 어디에서든 똑같이 동작합니다. 국가 코드와 플랜 옵션은 로케이션과 레지덴셜 프록시 페이지에서 확인할 수 있습니다.






