Anti-detect Firefox
Camoufox
Camoufox is a Firefox build that spoofs a complete, consistent fingerprint at the engine level. Set its identity with launch options (below) rather than with client-side newContext settings, which would fight the injected identity. A proxy is required, and by default the identity is matched to the proxy's location. POST /session is an alias for this browser.
- Launch
POST https://starter.cdpfleet.com/camoufox/session- Connect
firefox.connect(wsUrl, { headers: { 'x-api-key': KEY } })- Playwright client
- 1.60.x
- Versions now
- stable152.0.4 pinned150
- Required
proxy
Example
// npm install [email protected]
// Browsers run on cdpfleet, so no `npx playwright install` is needed.
import { firefox } from 'playwright';
const KEY = process.env.CDPFLEET_API_KEY;
// 1. Launch the browser
const res = await fetch('https://starter.cdpfleet.com/camoufox/session', {
method: 'POST',
headers: { 'x-api-key': KEY, 'content-type': 'application/json' },
body: JSON.stringify({
proxy: 'http://user:[email protected]:8080',
headless: false,
}),
});
if (!res.ok) throw new Error(`launch failed: ${res.status} ${await res.text()}`);
const { wsUrl } = await res.json();
// 2. Connect and drive it
const browser = await firefox.connect(wsUrl, { headers: { 'x-api-key': KEY } });
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
await browser.close(); // ends the session and stops billing
# pip install playwright==1.60.0 requests
# Browsers run on cdpfleet, so no `playwright install` is needed.
import os
import requests
from playwright.sync_api import sync_playwright
KEY = os.environ["CDPFLEET_API_KEY"]
# 1. Launch the browser
res = requests.post(
"https://starter.cdpfleet.com/camoufox/session",
headers={"x-api-key": KEY},
json={
"proxy": "http://user:[email protected]:8080",
"headless": False,
},
timeout=60,
)
res.raise_for_status()
ws_url = res.json()["wsUrl"]
# 2. Connect and drive it
with sync_playwright() as p:
browser = p.firefox.connect(ws_url, headers={"x-api-key": KEY})
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close() # ends the session and stops billing
// Maven: com.microsoft.playwright:playwright:1.60.0 and com.google.code.gson:gson:2.11.0
// Run with PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1: browsers run on cdpfleet.
import com.google.gson.JsonParser;
import com.microsoft.playwright.*;
import java.net.URI;
import java.net.http.*;
import java.util.Map;
public class Example {
public static void main(String[] args) throws Exception {
String key = System.getenv("CDPFLEET_API_KEY");
// 1. Launch the browser
String body = """
{
"proxy": "http://user:[email protected]:8080",
"headless": false
}
""";
HttpResponse<String> res = HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create("https://starter.cdpfleet.com/camoufox/session"))
.header("x-api-key", key)
.header("content-type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build(),
HttpResponse.BodyHandlers.ofString());
if (res.statusCode() != 200) {
throw new RuntimeException("launch failed: " + res.statusCode() + " " + res.body());
}
String wsUrl = JsonParser.parseString(res.body()).getAsJsonObject().get("wsUrl").getAsString();
// 2. Connect and drive it
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.firefox().connect(wsUrl,
new BrowserType.ConnectOptions().setHeaders(Map.of("x-api-key", key)));
Page page = browser.newPage();
page.navigate("https://example.com");
System.out.println(page.title());
browser.close(); // ends the session and stops billing
}
}
}
// dotnet add package Microsoft.Playwright --version 1.60.0
// Browsers run on cdpfleet, so no `playwright install` is needed.
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Microsoft.Playwright;
var key = Environment.GetEnvironmentVariable("CDPFLEET_API_KEY")!;
// 1. Launch the browser
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", key);
var body = """
{
"proxy": "http://user:[email protected]:8080",
"headless": false
}
""";
var res = await http.PostAsync("https://starter.cdpfleet.com/camoufox/session",
new StringContent(body, Encoding.UTF8, "application/json"));
if (!res.IsSuccessStatusCode)
throw new Exception($"launch failed: {(int)res.StatusCode} {await res.Content.ReadAsStringAsync()}");
var wsUrl = (await res.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("wsUrl").GetString()!;
// 2. Connect and drive it
using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Firefox.ConnectAsync(wsUrl, new BrowserTypeConnectOptions
{
Headers = new Dictionary<string, string> { ["x-api-key"] = key },
});
var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com");
Console.WriteLine(await page.TitleAsync());
await browser.CloseAsync(); // ends the session and stops billing
// go get github.com/playwright-community/[email protected]
//
// One-time driver setup (needs Node.js 18+): playwright-go can no longer download the
// 1.60.0 driver itself, so build it from npm and point PLAYWRIGHT_DRIVER_PATH at it:
// mkdir -p ~/.cache/pw-driver-1.60.0 && cd ~/.cache/pw-driver-1.60.0 &&
// curl -sL https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz | tar xz &&
// ln -sf "$(command -v node)" node
// export PLAYWRIGHT_DRIVER_PATH=~/.cache/pw-driver-1.60.0
// Browsers run on cdpfleet, so no browsers are downloaded (SkipInstallBrowsers).
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"github.com/playwright-community/playwright-go"
)
func main() {
key := os.Getenv("CDPFLEET_API_KEY")
// 1. Launch the browser
body := `{
"proxy": "http://user:[email protected]:8080",
"headless": false
}`
req, _ := http.NewRequest("POST", "https://starter.cdpfleet.com/camoufox/session", strings.NewReader(body))
req.Header.Set("x-api-key", key)
req.Header.Set("content-type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
msg, _ := io.ReadAll(res.Body)
log.Fatalf("launch failed: %s %s", res.Status, msg)
}
var session struct {
WsURL string `json:"wsUrl"`
}
if err := json.NewDecoder(res.Body).Decode(&session); err != nil {
log.Fatal(err)
}
// 2. Connect and drive it (Install fetches only the Playwright driver, once)
opts := &playwright.RunOptions{SkipInstallBrowsers: true}
if err := playwright.Install(opts); err != nil {
log.Fatal(err)
}
pw, err := playwright.Run(opts)
if err != nil {
log.Fatal(err)
}
defer pw.Stop()
browser, err := pw.Firefox.Connect(session.WsURL, playwright.BrowserTypeConnectOptions{
Headers: map[string]string{"x-api-key": key},
})
if err != nil {
log.Fatal(err)
}
page, err := browser.NewPage()
if err != nil {
log.Fatal(err)
}
if _, err := page.Goto("https://example.com"); err != nil {
log.Fatal(err)
}
title, _ := page.Title()
fmt.Println(title)
browser.Close() // ends the session and stops billing
}
Want different options or a specific version? Open this browser in the code builder.
Launch options
Send these as the JSON body of the launch request. Everything is optional except proxy; unknown fields are ignored. Context options such as viewport, user agent or cookies are set client-side after connecting (browser.newContext({...})) — but for Camoufox prefer the fingerprint options below.
| Option | Type | Default | What it does |
|---|---|---|---|
proxy | string | object | array | none — required | A proxy URL ( Required for every session: a launch without a proxy returns Example: "http://user:[email protected]:8080" |
proxy_rules | array | none | A list of Needs Example: [{"name":"dc-assets","hosts":["*.cloudfront.net","static.example.com"],"proxy":["http://u:[email protected]:4444","http://u:[email protected]:4444"]}] |
inactivity_timeout | number | string | 60s | The timer resets on any protocol traffic in either direction. A number of seconds ( A slow page load through a proxy can produce no traffic for a while, so keep it at 60 s or more. Shared threads can lower it but not raise it above 60 s; dedicated threads can set up to 24 h. Never longer than the session's overall timeout. Example: "2m" |
overall_timeout | number | string | your plan's maximum session length (24 h) | A number of seconds ( Example: "30m" |
headless | boolean | "virtual" | false (headful, on a virtual display) | Any truthy value runs headless — including the strings Example: false |
version | string | none (latest stable) | Request a retained previous major, e.g. Previous majors rotate out automatically: Chrome keeps its two most recent previous majors and drops the oldest within about a day of a new stable release; Camoufox keeps recent majors plus long-term ones. Read the live list rather than hard-coding a number. Send either Example: "152" |
os | "windows" | "macos" | "linux" | array | random among windows / macos / linux | The OS the spoofed fingerprint claims to run on. A list picks one at random per session. Required when Example: "windows" |
locale | string | array | derived from the proxy's exit IP | The first locale drives An unknown locale fails the launch. Example: "en-US" |
geoip | boolean | true | At launch the browser looks up its public IP through your proxy and aligns its identity with that location. Set If the proxy can't reach the IP lookup services, the launch fails. Example: false |
humanize | boolean | number | true | Moves the cursor along natural paths. A number sets the maximum movement duration in seconds. Set Example: 1.5 |
block_images | boolean | false | Saves bandwidth and time on image-heavy pages. Blocked images are themselves a detectable signal. Example: true |
block_webrtc | boolean | false | When left enabled (with Example: true |
block_webgl | boolean | false | Turns WebGL off. Overrides A missing WebGL is itself a fingerprint signal. Example: false |
disable_coop | boolean | false | Lets you click elements inside cross-origin iframes, such as some CAPTCHA widgets. Detectable. Example: true |
screen | object | none | Bounds for the generated screen: Ignored when Example: {"minWidth":1280,"maxWidth":1920,"minHeight":720,"maxHeight":1080} |
window | [width, height] | random | Use this window size instead of a random one, e.g. Ignored when Example: [1280,720] |
fingerprint | object | generated | Supply a full Firefox fingerprint object instead of a generated one. It must include A non-Firefox fingerprint returns 400. Example: {"navigator":{"userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0"},"screen":{}} |
config | object | none | A map of Camoufox fingerprint properties to values, e.g. Strictly validated: an unknown property (e.g. Chromium-only Example: {"navigator.hardwareConcurrency":8,"navigator.maxTouchPoints":0} |
fonts | array | the target OS's fonts | Font family names to expose in addition to the OS defaults. Example: ["Arial","Helvetica"] |
custom_fonts_only | boolean | false | Drop the target OS's default font list and expose only the families listed in Requires Example: true |
webgl_config | [vendor, renderer] | generated | Pick a WebGL identity from Camoufox's database, e.g. Requires Example: ["Intel","Intel(R) HD Graphics, or similar"] |
ff_version | number | the real Firefox major | Changes the version the fingerprint reports (user agent etc.), not the actual browser build. To run an older build, use A reported version that differs from the real engine is detectable. Example: 135 |
main_world_eval | boolean | false | Enables Main-world scripts are visible to the page. Example: true |