To run Camoufox with residential proxies, pass a Playwright-style proxy dict to Camoufox() or AsyncCamoufox(), set geoip=True so it reads timezone, locale and geolocation from the exit IP, and use a sticky session so the IP Camoufox looked up at launch is the one you browse from. Camoufox is a Firefox build with its fingerprint patches in C++, not a set of JavaScript overrides, so the work left to you is keeping the network identity and the browser identity consistent. The rest of this guide is the code, the checks behind it, and where Camoufox is the wrong tool.
What Camoufox is, and why it pairs with residential proxies
Camoufox is an open-source Firefox fork built for scraping and AI agents. You drive it with the normal Playwright API from Python (the camoufox package on PyPI) or, since September 2026, an official npm launcher. Its README says data is "intercepted at the C++ implementation level", so a page that inspects navigator, screen, WebGL or fonts sees native getters and not patched JavaScript functions.
That is a different approach from the Chromium-side tools. Patchright is a patched Playwright driver that leaves Chrome itself alone and stops the driver from doing detectable things over the Chrome DevTools Protocol (CDP), such as calling Runtime.enable. nodriver drops WebDriver entirely and talks CDP to a stock Chrome. Both make an automated Chrome look like Chrome. Camoufox makes an automated Firefox look like a range of Firefox installs, and Playwright controls it over Juggler, Firefox's own automation protocol, not CDP. The Camoufox patches give Juggler an isolated copy of the page, so Playwright's helpers (for example window.__playwright__binding__) are never visible to page scripts.
Residential proxies come in because Camoufox can't hide the IP. Every launch generates a new device identity: OS, screen, GPU, fonts, voices, language. If that identity says "home desktop in Germany" while the connection comes from a datacenter range in Virginia, the fingerprint work counts for nothing. The README says Camoufox "is intended to be used with rotating proxies (preferably residential IPs)".
Install Camoufox and fetch the browser
Installation is two steps: the Python package, then the patched Firefox binary, which is downloaded separately.
pip install -U "camoufox[geoip]"
python -m camoufox fetch
The [geoip] extra adds the geoip2 reader, and Camoufox downloads a geolocation dataset to go with it. The install docs call it optional but "heavily recommended if you are using proxies", and geoip=True won't run without it. Two version notes that matter when you pin dependencies:
- As of early October 2026 the current stable release on PyPI is
camoufox 0.5.6(6 September 2026), built against Firefox 152. Builds based on Firefox 156 were being published as GitHub prereleases and0.5.7b*betas at the end of September. Use stable unless you need a specific fix. - The package needs Python 3.10 or newer, and its
pyproject.tomlpinsplaywright<1.63because the server mode imports a private Playwright module. If something else in your project wants the newest Playwright, expect a conflict.
The Python API: sync, async and the options that matter
Camoufox wraps Playwright's Firefox launcher, so once you have the browser object every Playwright call works as usual. The sync and async entry points are context managers:
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())
These keyword arguments matter most for proxied work. The descriptions come from the usage docs and the launch_options() docstring in the pythonlib source:
proxy: a Playwright proxy dict (server,username,password). Ifgeoip=True, Camoufox also sends one request through this proxy at launch to learn the exit IP.geoip:Trueto look the exit IP up automatically, or a literal IP string. From that IP Camoufox sets latitude/longitude, timezone, country, locale and the WebRTC IP.geoip_dbchooses the database. On 30 September 2026 the main branch made "GeoIP AIO" the default and deprecated GeoLite2, so check which one your installed version uses.locale: an explicit locale such as"en-US", a country code such as"US"(Camoufox then picks a language from that country's distribution of speakers), or a list. The first entry drives theIntlAPI.humanize:True, or a float giving the maximum cursor-movement time in seconds. Stable 0.5.6 still draws Bézier curves. The main branch switched on 24 September 2026 to replaying recorded human mouse paths from the Cursory dataset, so you only get that with the Firefox 156 prereleases or later builds.headless:Falseby default.Trueuses Firefox headless, which Camoufox patches to look like a normal window."virtual"runs the browser headful inside an Xvfb virtual display. That option is Linux only and needs thexvfbpackage installed.os: limits the generated identity to"windows","macos","linux"or a list of them.block_webrtc,block_images,block_webgl: toggles. The project's own warnings say blocking WebGL or images has been reported to cause detection on major WAFs.
The virtual display page gives the reasoning for "virtual": Camoufox patches headless mode, but "running in headless mode may still be detectable in the future". On a Linux server, Xvfb costs a little extra memory and removes a whole class of headless tells. That's usually worth it.
A working example: country-targeted, sticky, coherent
The example below opens a multi-step session through the ProxyHat gateway with a German exit, lets Camoufox derive timezone and locale from the exit IP, and checks that the identity holds together before it does any real work. Country and session both go in the proxy username.
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:
# Sticky session + country target, both encoded in the username
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) # one IP for the whole flow
async with AsyncCamoufox(
proxy=proxy_for(country, session_id),
geoip=True, # timezone/locale/geo/WebRTC from exit IP
humanize=True, # humanized cursor movement
headless="virtual", # Linux + Xvfb; use headless=True elsewhere
os=["windows", "macos"],
) as browser:
page = await browser.new_page()
# 1. What does the outside world see?
await page.goto("https://ipinfo.io/json")
exit_info = await page.evaluate("() => JSON.parse(document.body.innerText)")
# 2. What does the browser claim?
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. Real multi-step work on the same IP
await page.goto("https://example.com/", wait_until="domcontentloaded")
# ... log in, paginate, submit, all on the same session_id
asyncio.run(run("DE"))
Why it is built this way:
- The sticky session is essential with
geoip=True. The lookup happens once, when the browser launches: Camoufox'spublic_ip()sends a request through your proxy to a public IP echo service and builds the identity from the answer. If the username has no session, a rotating gateway can give the next connection a different residential IP. In a country with one timezone that rarely shows. In the US, Russia, Brazil or Australia the second IP can be several timezones away from the one the browser reports. A sticky session makes the lookup IP and the browsing IP the same (more in our sticky vs rotating sessions guide). - One session ID per browser, not per page. Rotate by closing the browser and launching a new one with a new
session_id. That gives you a new IP and a new fingerprint together, which is what a different visitor looks like. - The ipinfo check is a cheap tripwire. Any IP-geolocation endpoint will do. The point is to compare what the network says with what the browser says before you spend requests on the target. Geolocation databases disagree at the edges, so treat a mismatch as a reason to relaunch, not as proof something is broken.
- Prefer the HTTP endpoint. Playwright's network docs document username/password only for HTTP(S) proxies. SOCKS5 (
gate.proxyhat.com:1080) is supported as a proxy type, but authenticated SOCKS through a browser is where setups tend to break. HTTP vs SOCKS5 covers the wider trade-offs.
Fingerprint coherence checklist
Coherence means exit IP country, timezone, language, WebRTC address, OS, fonts and GPU all describe one plausible machine in one place. Camoufox handles the browser side; most failures come from settings you override. Check these before scaling up:
- Exit IP country ↔ timezone. Check both
Intl.DateTimeFormat().resolvedOptions().timeZoneandDate.getTimezoneOffset(). Detection scripts compare the IP's geolocated timezone with the browser's, and a mismatch is one of the oldest proxy tells. Withgeoip=Trueon a sticky session they match by construction. - Exit IP country ↔ language.
navigator.language,navigator.languagesand theAccept-Languageheader should fit the country. Camoufox keeps the header andnavigatorin sync for you. If you passlocale=, pass a full locale with a region ("de-DE", not"de"): the project warns that a missing region gets generated for you and "can cause suspicion if your IP does not match your locale region". - WebRTC ↔ exit IP. WebRTC can expose your real local or public address and bypass the proxy. With
geoipenabled, Camoufox spoofs the WebRTC IPv4 at the protocol level to the exit IP and turns off IPv6 DNS. Leave WebRTC enabled and spoofed rather than blocked, because a desktop Firefox with no WebRTC at all is unusual. - Geolocation API ↔ IP. Camoufox fills latitude and longitude from the GeoIP lookup and sets the permission pref, so
navigator.permissions.query({name: "geolocation"})reports "granted" consistently with the position it hands out. Don't set geolocation inconfigby hand. The library warns that it "can lead to detection if your target geolocation does not match your IP". - OS ↔ fonts ↔ GPU. The generator draws from a statistical model of real traffic, and a coherence check rejects impossible combinations, such as a Windows user agent with an Apple M1 GPU. Avoid
custom_fonts_only=True, and only passwebgl_configpairs the model has seen on that OS (anything else raisesValueError). - User-Agent: leave it alone. Setting
User-Agentornavigatorfields manually is the quickest way to make the network and the JavaScript disagree. The library warns against both. - IP type ↔ claimed device. A generated consumer desktop on a hosting-provider ASN is an incoherent pair no matter how good the fingerprint is. This is where residential (or ISP) IPs justify their cost. Our browser fingerprinting explainer covers how these signals are combined server-side.
- One identity per session. Cookies, IP and fingerprint should all change together or not at all. A returning cookie on a new IP with a new GPU looks less like a person than either a stable session or a clean new one.
Camoufox vs Patchright vs nodriver: which to pick
Pick by engine first. If your target mostly sees Chrome traffic, or checks for V8 behaviour, use a Chromium tool. If you need deep fingerprint rotation without writing JavaScript patches and Firefox traffic is acceptable, use Camoufox. The table summarises the trade-offs as of October 2026.
| Camoufox | Patchright | nodriver | |
|---|---|---|---|
| Engine | Patched Firefox (Gecko + SpiderMonkey) | Chromium / Chrome (unpatched browser) | Your installed Chrome, Chromium, Edge or Brave |
| Control protocol | Juggler (patched, isolated from page) | CDP, with Runtime.enable avoided via isolated worlds | CDP directly, no WebDriver |
| API | Playwright (Python, JS/TS) | Playwright drop-in (Python, Node, community .NET) | Own async Python API |
| TLS fingerprint origin | Firefox's own NSS stack, so a Firefox ClientHello | Real Chrome's BoringSSL, especially with channel="chrome" | Real Chrome's BoringSSL |
| Fingerprint rotation | Built in, generated per launch at C++ level | None. Recommends real Chrome without injection | None built in |
| Maintenance (Oct 2026) | Active again. Stable 0.5.6 in Sep 2026, but no stable releases between March 2025 and January 2026 | Tracks Playwright. v1.63.0 driver released Sep 2026 | Last PyPI release 0.50.3, May 2026 |
| Typical failure modes | Firefox-specific scrutiny, engine checks that only Chrome passes, fingerprint inconsistencies that vendors find and add to their checks | Console API disabled, Chromium-only, generic headless and behaviour signals still apply | Main-world evaluation is observable, no fingerprint management, smaller API surface than Playwright |
The TLS row matters more than it looks. All three use a real browser network stack, so none has the mismatch you get when Python's requests claims to be Chrome. But Camoufox will always present a Firefox handshake. If a site's model expects Chrome-shaped TLS for most of its traffic, see our TLS fingerprinting explainer on how JA3/JA4 are used for that kind of profiling.
Honest limitations
Camoufox is good at one thing, a coherent and rotating Firefox identity with no JavaScript patching, and it comes with real costs.
Firefox is a small slice of traffic
StatCounter puts Firefox at about 6.1% of worldwide desktop browsing in September 2026, against roughly 65% for Chrome. A small population is easier to model, and any anomaly within it stands out more. Some sites challenge non-Chromium browsers more readily. You won't know whether your target does until you test it with your own traffic.
It can't impersonate Chrome
The README says it directly: Camoufox "does not fully support injecting Chromium fingerprints", and some WAFs "test for Spidermonkey engine behavior, which is impossible to spoof". If the site insists on Chrome, no Camoufox configuration will get you there.
Maintenance has been uneven
The release history shows the gap. The last stable build before the pause was v135.0.1-beta.24 on 15 March 2025. The original author paused work, and apart from occasional community commits nothing shipped until a Firefox 146 beta in January 2026. The README then named Clover Labs as the official maintainer. Since then the project has moved quickly: a Firefox 150 build in May, Firefox 152 stable builds from July, an official TypeScript launcher in September and Firefox 156 prereleases by month end. The README still carries the warning "This project is under development. It may not be suitable for stable production use". Pin versions, and test an upgrade before rolling it out.
Fingerprints are a moving target
The project says openly that anti-bot vendors "test Camoufox over and over again to find even 1 unique inconsistency" and then add a check for it. The humanized cursor "may still be detected with sophisticated enough analysis". C++-level spoofing removes a whole category of tells, but it doesn't make you undetectable for good.
Resource cost
The project claims its debloated build runs at around 200 MB and uses less memory than stock Firefox. That's the project's figure, not ours, and your pages will add to it. It's still a full browser per identity. Plan for one browser per concurrent session rather than many contexts sharing a fingerprint, and use plain HTTP clients for endpoints that don't need JavaScript.
What Camoufox does not solve
- IP reputation. A flagged residential IP is still flagged. Test exits before you use them, for example with our free proxy checker.
- Rate limits and behaviour. Hundreds of identical paths a minute look automated whatever the fingerprint.
- CAPTCHAs, logins and access rules. Camoufox doesn't give you permission to collect anything. Respect robots.txt and the site's terms, stay away from data behind authentication you're not entitled to, and treat personal data as GDPR/CCPA-regulated even when it's publicly visible.
A sensible rollout
Start with one country and one sticky session. Log the coherence check output for every launch, and compare block rates against a baseline with Patchright on the same target before you commit to one engine. If Camoufox wins, scale by adding browsers with fresh session IDs, not by sharing one browser across sessions. If it loses on a site that clearly prefers Chrome, the table above tells you where to go next. The residential gateway works the same with any of the three tools. Country codes and plan options are on the locations and residential proxy pages.






