Compare / browsers
Firefox vs Camoufox
Stock Firefox against the anti-detect fork built on it: what Camoufox changes in what a site sees, what it costs you as a developer, and when plain Firefox is the better tool.
Firefox on the fleet is Playwright's own build (150 today): Mozilla's engine with the patches Playwright needs to drive it, nothing hidden, nothing spoofed. Camoufox (152) is a fork of that same engine whose purpose is the opposite: to look like a particular real person's computer — operating system, screen, GPU, fonts, locale, timezone and location generated at launch and answered consistently from inside the engine. Same Playwright client (firefox), same rendering engine, same TLS stack; very different answers to "who are you?". Both were launched headful through the same proxy for this page.
As a developer: driving them
| Firefox | Camoufox | |
|---|---|---|
| Build | Playwright's Firefox 150 (firefox endpoint, headless: false or true). | Camoufox 152 (camoufox endpoint; headless: false virtual display, or true); pinned version: "150" available. |
| Launch options | The common set: proxy, proxy_rules, headless, timeouts. | The common set plus the persona: os, locale, geoip, humanize, screen, window, fonts, webgl_config, fingerprint/config, ff_version, block_images/block_webrtc/block_webgl, main_world_eval. |
Where page.evaluate runs | In the page's world (measured): a page <script> sees globals you set. | In an isolated world (measured): the page can't see your globals and your checks don't see the persona unless you set main_world_eval: true and prefix expressions with mw: (measured: works). |
addInitScript | Visible to page scripts (measured). | Runs in the isolated world; page scripts don't see it (measured). Use config for identity values instead of init-script patches. |
| Console events, routing, screenshots, tracing | Work. | Work. |
| PDF, mobile emulation | Neither: page.pdf is Chromium-only and Firefox has no isMobile. | Neither, same engine. |
| Threads | 2 headful, 1 headless. | 2 headful (virtual display), 1 headless. |
As a scraper: what the site sees
| Firefox | Camoufox | |
|---|---|---|
| TLS and HTTP/2 | Firefox's: JA4 t13d1617h2_…, Firefox HTTP/2 SETTINGS and header order. | Identical — same NSS stack, same JA4, same HTTP/2 fingerprint (measured). Whatever OS the persona claims, real Firefox sends exactly this on every OS, so it never contradicts the user agent. |
navigator.webdriver | true (measured). Playwright's Firefox build announces automation; bot.sannysoft.com marks "WebDriver" red. | false (measured). |
| Who it says it is | Firefox 150 on Linux x86_64, navigator.languages empty, 1280×720 screen, the server's 64 cores, llvmpipe (Mesa) software renderer, 0 plugins, no PDF viewer — a Linux server running an automated browser, in plain sight. | Firefox 152 on a generated desktop (macOS in this run: MacIntel, 1440×900×30-bit screen, "Apple M1, or similar" GPU, 2 cores, 5 plugins, PDF viewer) — a consumer machine, different each launch unless you pin os/fingerprint. |
| Locale and time | UTC, no languages. Set locale and timezoneId in newContext yourself. | geoip matched the proxy exit: this run's exit was in Morocco and the browser reported Africa/Casablanca and tzm-MA without any code (Spain and Brazil in other runs). |
| Canvas | Deterministic for the build. | Different hash from Firefox (measured) — the persona's fonts and rendering, consistent within a session. |
| bot.sannysoft.com | 4 of 57 checks red: WebDriver, "Chrome (New)", two plugin checks. | 1 red: "Chrome (New)" — it is Firefox and says so. (The page rendered fewer rows in Camoufox before our 4-second cut-off; see raw output.) |
| The limit | Honest Linux automation: fine for rendering tests, screenshots and sites that don't care; the webdriver flag alone fails most bot checks. | Cannot be Chromium: sites that require Chrome-only behaviour or the Google Chrome client-hint brand see Firefox. Firefox-blocking sites block it too. |
Which to pick
- Firefox when you want Mozilla's engine as-is — cross-engine rendering checks, Gecko-specific bugs, a second opinion next to Chromium — and nobody is scoring the visitor. It is the cheaper mental model: what you set is what the page sees.
- Camoufox whenever the site scores visitors:
webdriveroff, a consumer-hardware story, locale and clock that match the proxy exit, and the whole persona generated for you. Budget for the isolated-world rule (main_world_eval) and for Firefox-only behaviour. - Neither for Chromium-only sites — use Chrome or Patchright; see Camoufox vs Patchright for that choice.
Measured on the fleet (2026-10-05)
One headful launch of each through the same rotating residential proxy. Camoufox's persona (OS, screen, GPU, cores, locale, timezone) is regenerated every launch; Firefox's values are the server's. navigator.languages came back empty for Firefox over the Playwright connection.
Launch and cost
Single run; timings depend on the proxy and on fleet load.
| Firefox | Camoufox | |
|---|---|---|
| Launch body | {"headless":false} | {"headless":false,"main_world_eval":true} |
| Threads used | 2 (linux_headful) | 2 (linux_headful) |
| Browser version | 150.0.2 | 152.0.4-beta.30 |
| Launch request → browser ready | 2.8 s | 5.8 s |
| Playwright connect | 1.4 s | 0.0 s |
| Load example.com | 0.9 s | 0.8 s |
| Load tls.peet.ws/api/all | 0.8 s | 4.7 s |
What the network sees
From tls.peet.ws/api/all, requested by the browser itself. JA3 hashes change between runs of the same build (GREASE), so compare JA4 and the HTTP/2 fingerprint.
| Firefox | Camoufox | |
|---|---|---|
| HTTP version | h2 | h2 |
| JA4 | t13d1617h2_86a278354501_3cbfd9057e0d | t13d1617h2_86a278354501_3cbfd9057e0d |
| Peetprint hash | fd4547eeb41f073156b7bc8125a79a3c | fd4547eeb41f073156b7bc8125a79a3c |
| Akamai HTTP/2 fingerprint | 1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s | 1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s |
| Cipher suites / extensions | 16 / 17 | 16 / 17 |
| Header order (first 12) | method, path, authority, scheme, user-agent, accept, accept-language, accept-encoding, upgrade-insecure-requests, sec-fetch-dest, sec-fetch-mode, sec-fetch-site | method, path, authority, scheme, user-agent, accept, accept-language, accept-encoding, upgrade-insecure-requests, sec-fetch-dest, sec-fetch-mode, sec-fetch-site |
What page scripts see
Read with page.evaluate on example.com. Cores and memory are the server's and vary between fleet nodes.
| Firefox | Camoufox | |
|---|---|---|
| navigator.userAgent | Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:152.0) Gecko/20100101 Firefox/152.0 |
| navigator.platform / vendor | Linux x86_64 / "" | MacIntel / "" |
| Languages | — | tzm-MA, tzm |
| Timezone / locale | UTC / en-GB | Africa/Casablanca / en-GB |
| Screen (w×h×availW×availH×depth) | 1280x720x1280x720x24 | 1440x900x1440x900x30 |
| Window outer / inner / DPR | 1280x720 / 1280x720 / 1 | 1440x900 / 1280x720 / 1 |
| hardwareConcurrency / deviceMemory | 64 / — | 2 / — |
| WebGL vendor | Mesa | Apple |
| WebGL renderer | llvmpipe, or similar | Apple M1, or similar |
| WebGL version | WebGL 2.0 | WebGL 2.0 |
| WebGPU (navigator.gpu) | no | no |
| Canvas hash | 71eb6917 | 384a49dd |
| navigator.webdriver | yes | no |
| window.chrome / navigator.connection | no / no | no / no |
| Plugins / PDF viewer | 0 / no | 5 / yes |
| Notification.permission / permissions.query | default / prompt | default / prompt |
Web platform features
Presence probes; a user agent that claims one version while exposing another's features is a detectable inconsistency.
| Firefox | Camoufox | |
|---|---|---|
Iterator.prototype.includes (new in Chrome 154) | no | no |
FontFace.width (new in Chrome 154) | no | no |
CSS text-decoration-inset (new in Chrome 154) | yes | yes |
Iterator.prototype.join (Chrome 153) | no | no |
Iterator.zip (Chrome 153) | yes | yes |
CSS scroll-axis-lock (Chrome 153) | no | no |
<camera> element (Chrome 153) | no | no |
document.requestStorageAccessFor (deprecated in 153) | no | no |
Topics API (document.browsingTopics) | no | no |
Protected Audience (navigator.joinAdInterestGroup) | no | no |
Shared Storage (window.sharedStorage) | no | no |
Attribution Reporting (window.attributionReporting) | no | no |
| Private Aggregation | no | no |
WebHID (navigator.hid) | no | no |
CSS margin-trim (Chrome 155) | no | no |
CSS text-decoration-skip-spaces (Chrome 155) | no | no |
CSS corner shorthand (Chrome 155) | no | no |
CSS symbols() (Chrome 155) | yes | yes |
Element.streamAppendHTML (Chrome 155) | no | no |
WebTransport.reliability (Chrome 155) | yes | yes |
window.maximize() — Additional Windowing Controls (Chrome 155) | no | no |
Canvas srgb-linear colour space (Chrome 155) | no | no |
Permission policy media-playback-while-not-visible (Chrome 155) | no | no |
bot.sannysoft.com
The classic headless-detection test page; what it marks red.
| Firefox | Camoufox | |
|---|---|---|
| Checks / failed | 57 / 4 | 37 / 1 |
| Failed checks | WebDriver (New), Chrome (New), Plugins Length (Old), Plugins is of type PluginArray | Chrome (New) |
A plain-HTTP URL (http://ip-api.com/json)
Chrome 154 warns before loading insecure HTTP by default — does that reach an automated session?
| Firefox | Camoufox | |
|---|---|---|
| Final URL | http://ip-api.com/json/?fields=status | http://ip-api.com/json/?fields=status |
| HTTP status | 200 | 200 |
| Content loaded / warning page first | yes / no | yes / no |
Playwright over the remote connection
Which client features work on this side.
| Firefox | Camoufox | |
|---|---|---|
| page.evaluate runs in the page's own world | yes | no |
main_world_eval option (mw: prefix) | — | yes |
| addInitScript visible to page scripts | yes | no |
| Console events | yes | yes |
| Request interception (page.route) | yes | yes |
| Screenshot | yes | yes |
| no — Error: page.pdf: PDF generation is only supported for Headless Chromium | no — Error: page.pdf: PDF generation is only supported for Headless Chromium | |
| Mobile device emulation | no — Error: browser.newContext: options.isMobile is not supported in Firefox | no — Error: browser.newContext: options.isMobile is not supported in Firefox |
| Tracing | yes | yes |
Rows where the two sides differ are highlighted. IP addresses are replaced with placeholders (203.0.113.x). The probe is compare/probe.mjs in our repository; the raw result is below.
Raw output
{
"ran_at": "2026-10-05T21:03:08.264Z",
"sides": [
{
"label": "Firefox",
"engine": "firefox",
"body": {
"headless": false
},
"launch": {
"browser": "firefox",
"resource_class": "linux_headful",
"weight": 2,
"launch_ms": 2796,
"connect_ms": 1403,
"example_ms": 922,
"peet_ms": 823
},
"network": {
"http_version": "h2",
"ja4": "t13d1617h2_86a278354501_3cbfd9057e0d",
"ja3_hash": "6447ab086255d194909d4013b1a89e87",
"peetprint_hash": "fd4547eeb41f073156b7bc8125a79a3c",
"ciphers": 16,
"extensions": 17,
"akamai_h2": "1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s",
"akamai_h2_hash": "6ea73faa8fc5aac76bded7bd238f6433",
"header_order": [
"method",
"path",
"authority",
"scheme",
"user-agent",
"accept",
"accept-language",
"accept-encoding",
"upgrade-insecure-requests",
"sec-fetch-dest",
"sec-fetch-mode",
"sec-fetch-site"
]
},
"page": {
"user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0",
"platform": "Linux x86_64",
"vendor": "",
"languages": [
"undefined"
],
"hardware_concurrency": 64,
"device_memory": null,
"max_touch_points": 0,
"webdriver": true,
"plugins": 0,
"pdf_viewer": false,
"ua_data": null,
"screen": "1280x720x1280x720x24",
"window": {
"outer": "1280x720",
"inner": "1280x720",
"dpr": 1
},
"timezone": "UTC",
"locale": "en-GB",
"webgl": {
"vendor": "Mesa",
"renderer": "llvmpipe, or similar",
"version": "WebGL 2.0"
},
"canvas_hash": "71eb6917",
"webgpu": false,
"chrome_object": false,
"chrome_runtime": false,
"notification_permission": "default",
"notification_query": "prompt",
"has_focus": true,
"connection_api": false,
"stack_read_by_debugger": false,
"features": {
"iterator_includes": false,
"iterator_join": false,
"iterator_zip": true,
"fontface_width": false,
"css_text_decoration_inset": true,
"css_scroll_axis_lock": false,
"websocket_options_bag": null,
"camera_element": false,
"topics_api": false,
"protected_audience": false,
"shared_storage": false,
"attribution_reporting": false,
"request_storage_access_for": false,
"webhid": false,
"private_aggregation": false,
"css_margin_trim": false,
"css_text_decoration_skip_spaces": false,
"css_corner_shorthand": false,
"css_symbols": true,
"stream_append_html": false,
"webtransport_reliability": true,
"window_maximize": false,
"media_display_state": true,
"canvas_srgb_linear": false,
"policy_media_playback_while_not_visible": false
}
},
"page_main_world": null,
"sannysoft": {
"checks": 57,
"failed": 4,
"failed_names": [
"WebDriver (New)",
"Chrome (New)",
"Plugins Length (Old)",
"Plugins is of type PluginArray"
],
"passed": 26
},
"plain_http": {
"final_url": "http://ip-api.com/json/?fields=status",
"status": 200,
"title": "",
"loaded": true,
"warning_page": false,
"page_text": "{\"status\":\"success\"}"
},
"playwright": {
"evaluate_in_main_world": true,
"console_events": true,
"init_script": true,
"route": true,
"screenshot": true,
"pdf": false,
"pdf_error": "Error: page.pdf: PDF generation is only supported for Headless Chromium",
"mobile_emulation": false,
"mobile_emulation_error": "Error: browser.newContext: options.isMobile is not supported in Firefox",
"tracing": true
},
"browser_version": "150.0.2"
},
{
"label": "Camoufox",
"engine": "camoufox",
"body": {
"headless": false,
"main_world_eval": true
},
"launch": {
"browser": "camoufox",
"resource_class": "linux_headful",
"weight": 2,
"launch_ms": 5833,
"connect_ms": 28,
"example_ms": 758,
"peet_ms": 4685
},
"network": {
"http_version": "h2",
"ja4": "t13d1617h2_86a278354501_3cbfd9057e0d",
"ja3_hash": "6447ab086255d194909d4013b1a89e87",
"peetprint_hash": "fd4547eeb41f073156b7bc8125a79a3c",
"ciphers": 16,
"extensions": 17,
"akamai_h2": "1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s",
"akamai_h2_hash": "6ea73faa8fc5aac76bded7bd238f6433",
"header_order": [
"method",
"path",
"authority",
"scheme",
"user-agent",
"accept",
"accept-language",
"accept-encoding",
"upgrade-insecure-requests",
"sec-fetch-dest",
"sec-fetch-mode",
"sec-fetch-site"
]
},
"page": {
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:152.0) Gecko/20100101 Firefox/152.0",
"platform": "MacIntel",
"vendor": "",
"languages": [
"tzm-MA",
"tzm"
],
"hardware_concurrency": 2,
"device_memory": null,
"max_touch_points": 0,
"webdriver": false,
"plugins": 5,
"pdf_viewer": true,
"ua_data": null,
"screen": "1440x900x1440x900x30",
"window": {
"outer": "1440x900",
"inner": "1280x720",
"dpr": 1
},
"timezone": "Africa/Casablanca",
"locale": "en-GB",
"webgl": {
"vendor": "Apple",
"renderer": "Apple M1, or similar",
"version": "WebGL 2.0"
},
"canvas_hash": "384a49dd",
"webgpu": false,
"chrome_object": false,
"chrome_runtime": false,
"notification_permission": "default",
"notification_query": "prompt",
"has_focus": true,
"connection_api": false,
"stack_read_by_debugger": false,
"features": {
"iterator_includes": false,
"iterator_join": false,
"iterator_zip": true,
"fontface_width": false,
"css_text_decoration_inset": true,
"css_scroll_axis_lock": false,
"websocket_options_bag": null,
"camera_element": false,
"topics_api": false,
"protected_audience": false,
"shared_storage": false,
"attribution_reporting": false,
"request_storage_access_for": false,
"webhid": false,
"private_aggregation": false,
"css_margin_trim": false,
"css_text_decoration_skip_spaces": false,
"css_corner_shorthand": false,
"css_symbols": true,
"stream_append_html": false,
"webtransport_reliability": true,
"window_maximize": false,
"media_display_state": true,
"canvas_srgb_linear": false,
"policy_media_playback_while_not_visible": false
}
},
"page_main_world": {
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:152.0) Gecko/20100101 Firefox/152.0",
"platform": "MacIntel",
"vendor": "",
"languages": [
"tzm-MA",
"tzm"
],
"hardware_concurrency": 2,
"device_memory": null,
"max_touch_points": 0,
"webdriver": false,
"plugins": 5,
"pdf_viewer": true,
"ua_data": null,
"screen": "1440x900x1440x900x30",
"window": {
"outer": "1440x900",
"inner": "1280x720",
"dpr": 1
},
"timezone": "Africa/Casablanca",
"locale": "en-GB",
"webgl": {
"vendor": "Apple",
"renderer": "Apple M1, or similar",
"version": "WebGL 2.0"
},
"canvas_hash": "384a49dd",
"webgpu": false,
"chrome_object": false,
"chrome_runtime": false,
"notification_permission": "default",
"notification_query": "prompt",
"has_focus": true,
"connection_api": false,
"stack_read_by_debugger": false,
"features": {
"iterator_includes": false,
"iterator_join": false,
"iterator_zip": true,
"fontface_width": false,
"css_text_decoration_inset": true,
"css_scroll_axis_lock": false,
"websocket_options_bag": null,
"camera_element": false,
"topics_api": false,
"protected_audience": false,
"shared_storage": false,
"attribution_reporting": false,
"request_storage_access_for": false,
"webhid": false,
"private_aggregation": false,
"css_margin_trim": false,
"css_text_decoration_skip_spaces": false,
"css_corner_shorthand": false,
"css_symbols": true,
"stream_append_html": false,
"webtransport_reliability": true,
"window_maximize": false,
"media_display_state": true,
"canvas_srgb_linear": false,
"policy_media_playback_while_not_visible": false
}
},
"sannysoft": {
"checks": 37,
"failed": 1,
"failed_names": [
"Chrome (New)"
],
"passed": 9
},
"plain_http": {
"final_url": "http://ip-api.com/json/?fields=status",
"status": 200,
"title": "",
"loaded": true,
"warning_page": false,
"page_text": "{\"status\":\"success\"}"
},
"playwright": {
"evaluate_in_main_world": false,
"console_events": true,
"init_script": false,
"route": true,
"screenshot": true,
"pdf": false,
"pdf_error": "Error: page.pdf: PDF generation is only supported for Headless Chromium",
"mobile_emulation": false,
"mobile_emulation_error": "Error: browser.newContext: options.isMobile is not supported in Firefox",
"tracing": true,
"main_world_eval_option": true
},
"browser_version": "152.0.4-beta.30"
}
]
}Bottom line
Camoufox is Firefox with one job added: to make every identity question agree with a believable machine and with the proxy. It keeps Firefox's network fingerprint — which is a feature, since Firefox's TLS is the same on every OS — and replaces everything above it.
If you were about to patch stock Firefox with newContext options and init scripts to hide webdriver, fix the GPU string and set a timezone, that work is what Camoufox already did, inside the engine where a page can't inspect it.