cdpfleet / Bot protection
Measured, dated
What gets through where.
A week of measurements on the bot-protection vendors' own pages: headless Chromium, headful Chrome and Edge, and Camoufox, through your own proxy and residential exits, one load per cell, nothing clicked. Dated snapshots, not promises.
- 4vendors, on their own sites
- 5languages per case
- 0CAPTCHAs solved
The matrix
| Vendor (page) | Headless Chromium | Headful Chrome / Edge | Camoufox | Notes — measured |
|---|---|---|---|---|
| Cloudflare challenge (scrapingcourse.com) | challenge, every cell | challenge, every cell | cleared by itself in 1–18 s | Network and tier changed nothing; the browser decided. Case 64, 2026-10-21 |
| Cloudflare background check (nowsecure.nl) | content | content | content | Every setup got the page and a cf_clearance cookie. Case 64, 2026-10-21 |
| DataDome (datadome.co) | blocked (403) 19/20 | mostly content, CAPTCHA in some runs | mostly content, CAPTCHA in some runs | Decided on the first response; no consistent network effect. Case 65, 2026-10-22 |
| Akamai Bot Manager (akamai.com) | blocked (403) 10/10 | content, every cell | content, every cell | _abck stayed unvalidated and the page was served anyway. Case 67, 2026-10-23 |
| HUMAN (humansecurity.com) | press-and-hold 10/10 | content 10/10 on own proxy, 10/15 on residential | press-and-hold 12/15 | HUMAN and Akamai disagreed about Camoufox. Case 67, 2026-10-23 |
| reCAPTCHA v3 / hCaptcha demos | score 0.9 / widget shown | score 0.9 / widget shown | score 0.9 / widget shown | The demos don't separate browsers. Case 68, 2026-10-24 |
One load per cell, nothing clicked, five runs per case (one per language). Outcomes: content (the real page), cleared (an automatic check passed by itself), challenge (a CAPTCHA or press-and-hold), blocked (a block page). Camoufox is based on Firefox source; cdpfleet is not affiliated with Mozilla.
Where to start, per vendor
- Cloudflare: start with Camoufox when a page shows Cloudflare's interstitial. On scrapingcourse.com it cleared by itself in 1–18 s while headless Chromium, headful Chrome and Edge stayed on the challenge in every cell (case 64, 2026-10-21). A profile that had cleared once loaded the page straight away on the next visit from the same exit.
- DataDome: start headful (Chrome, Edge or Camoufox) and keep a
profile. Headless Chromium was blocked 19 times in 20; the headful setups mostly got the page, and a returning profile never did worse than its first visit, turning Chrome's CAPTCHA into content in 3 of 3 runs (case 65, 2026-10-22). - Akamai: any headful browser. Headful Chrome, Edge and Camoufox got akamai.com in every cell; only headless Chromium was turned away (case 67, 2026-10-23).
- HUMAN: headful Chrome or Edge, best on your own proxy (content in 10 of 10 cells there, 10 of 15 on residential exits). Camoufox met the press-and-hold in 12 of 15 cells (case 67, 2026-10-23).
- Unknown vendor: find out first. The cookies and request hosts each vendor leaves (
__cf_bm,datadome,_abck,_px*and so on) identified it on 19–20 of 20 public sites (case 62, 2026-10-20).
Camoufox, headful, on a sticky residential exit, with a profile that keeps its cookies
// 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: 'cdpfleet-resi-country-us-session-shop1-lifetime-30',
headless: false,
geoip: true,
profile: 'shop-visitor',
}),
});
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 context = browser.contexts()[0]; // the saved profile — a new context isn't saved
const page = context.pages()[0] ?? await context.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": "cdpfleet-resi-country-us-session-shop1-lifetime-30",
"headless": False,
"geoip": True,
"profile": "shop-visitor",
},
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})
context = browser.contexts[0] # the saved profile — a new context isn't saved
page = context.pages[0] if context.pages else context.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": "cdpfleet-resi-country-us-session-shop1-lifetime-30",
"headless": false,
"geoip": true,
"profile": "shop-visitor"
}
""";
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)));
BrowserContext context = browser.contexts().get(0); // the saved profile — a new context isn't saved
Page page = context.pages().isEmpty() ? context.newPage() : context.pages().get(0);
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": "cdpfleet-resi-country-us-session-shop1-lifetime-30",
"headless": false,
"geoip": true,
"profile": "shop-visitor"
}
""";
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 context = browser.Contexts[0]; // the saved profile — a new context isn't saved
var page = context.Pages.Count > 0 ? context.Pages[0] : await context.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": "cdpfleet-resi-country-us-session-shop1-lifetime-30",
"headless": false,
"geoip": true,
"profile": "shop-visitor"
}`
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)
}
context := browser.Contexts()[0] // the saved profile — a new context isn't saved
var page playwright.Page
if pages := context.Pages(); len(pages) > 0 {
page = pages[0]
} else if page, err = context.NewPage(); 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
}
Why outcomes vary by site and day
- Each site sets its own rules. The same vendor can be strict on one site and lenient on another, which is why these cases use the vendors' own pages and say "on these pages on this date" (case 65, case 67).
- Vendors change their checks all the time. Every result here is a dated snapshot.
- The network mattered less than the browser. Tier 1 vs Tier 4 never turned a Chromium challenge into a pass on Cloudflare (case 64), DataDome showed no consistent network effect (case 65), and the rotating own proxy accounted for most of the demo-page misses (case 68).
- Not every visit clears. Camoufox's first visit to the Cloudflare challenge stayed on it in two later runs, so there was nothing for a profile to skip (case 64).
What didn't help
- VPN Tunnel didn't change a verdict. It makes WebRTC show the exit IP and enables HTTP/3, but Cloudflare's and DataDome's verdicts were the same with and without it on these pages (case 69, 2026-10-24).
humanizemade no measurable difference on DataDome's pages: the page after the cursor moves loaded 5 of 5 times with it on and 4 of 5 with it off (case 65, 2026-10-22).- CAPTCHA demo scores don't rank browsers. Google's v3 demo gave every setup 0.9, headless Chromium included, the same browser the real protections blocked (case 68, 2026-10-24).
- Bot-test pages aren't the target site. Public fingerprint test pages flagged headless Chromium everywhere and disagreed about the rest (case 63, 2026-10-20). Judge a setup on the site you care about.
When to stop
- These cases measure; they don't defeat. Every case loads public pages once per setup, clicks nothing and solves no CAPTCHA. When a site shows a challenge, that is the site's answer.
- Don't farm CAPTCHAs or hammer a site. Retrying until something passes wears the site down and burns your exits. Back off, honour
Retry-After(case 53), or hand the page to a person in live view. - Respect the site. Read its terms, prefer its API or a data feed where one exists, and keep your request rate polite. No setup here is a promise of access.