[Examples & benchmarks](https://cdpfleet.com/docs/cases) / #75 · 2026-10-29 · Benchmark · Medium · for [web scraping](https://cdpfleet.com/web-scraping)

# HUMAN on real sites: twelve browsers, three sites, one exit each

Three real sites behind HUMAN (formerly PerimeterX), chosen from our sweep as easy, medium and hard, loaded once by each of twelve browsers and by a returning Camoufox profile, through one sticky residential exit per site, with the HUMAN cookies each load got. Dated 2026-10-29.

[Google Chrome](https://cdpfleet.com/docs/browsers/chrome) [Microsoft Edge](https://cdpfleet.com/docs/browsers/edge) [Brave](https://cdpfleet.com/docs/browsers/brave) [Opera](https://cdpfleet.com/docs/browsers/opera) [Yandex Browser](https://cdpfleet.com/docs/browsers/yandex) [Naver Whale](https://cdpfleet.com/docs/browsers/whale) [Chromium](https://cdpfleet.com/docs/browsers/chromium) [Patchright](https://cdpfleet.com/docs/browsers/patchright) [Firefox](https://cdpfleet.com/docs/browsers/firefox) [WebKit](https://cdpfleet.com/docs/browsers/webkit) [Camoufox](https://cdpfleet.com/docs/browsers/camoufox)

Run on production on 2026-10-29: ✓ Node.js ✓ Python ✓ Java ✓ C# ✓ Go

A benchmark: the results below are what this setup measured on the dates shown, on our own test runs. Sites, networks and browsers change, so rerun the code before you rely on the numbers.

## The problem

HUMAN's own site ([case 67](https://cdpfleet.com/docs/cases/akamai-and-human-what-gets-through)) shows its Press & Hold challenge, but every real site sets its own rules. This case takes three real sites behind HUMAN from our sweep of production sites, one that let headless Chrome in (easy), one that stopped headless Chrome but not Camoufox (medium) and one that stopped Camoufox too (hard), and loads each once with every browser the fleet offers, plus a returning Camoufox profile, through one sticky residential exit per site, as [case 70](https://cdpfleet.com/docs/cases/cloudflare-real-sites-by-browser), [case 71](https://cdpfleet.com/docs/cases/datadome-real-sites-by-browser) and [case 73](https://cdpfleet.com/docs/cases/akamai-real-sites-by-browser) did for Cloudflare, DataDome and Akamai. It also records which HUMAN cookies (`_px3`, `_pxvid`, `_pxhd`, `pxcts` …) each load got, by name only. The sites aren't named: they stand for a kind of site, and their rules can change any day. It is a dated measurement, not an evasion guide; for the signals a page can read, see [What a page can read about your browser](https://cdpfleet.com/docs/fingerprinting), and for the whole picture [/bot-protection](https://cdpfleet.com/bot-protection).

## What we used, and why

| What | Why |
| --- | --- |
| Twelve browsers | Chrome headful and headless, Edge, Brave, Opera, Yandex, Whale, Chromium headless and Patchright (all Chromium), Firefox, WebKit, and Camoufox (based on Firefox source; not affiliated with Mozilla). Rebrowser needs a Playwright 1.52 client, so it's left out of a program written for 1.60. |
| `proxy`: `cdpfleet-resi-country-us-session-<id>` | One sticky residential exit per site, shared by every setup on that site, so a difference between rows is the browser. |
| One session at a time per site | Each site's setups run one after another; the three sites run side by side. |
| `page.evaluate()` after 8 s | HUMAN's Press & Hold widget (`#px-captcha`), or a short page asking to verify you are human, is `challenge` — usually on a page titled "Access to this page has been denied", but interactive, so not a block; an "access denied" or "has been blocked" page without it, or an error status with a short page, is `blocked`; anything else `content`. Nothing is clicked. |
| `context.cookies()` | The names of the cookies HUMAN set (`_px3`, `_pxvid`, `_pxhd`, `_pxde`, `pxcts` …) 8 s after the load. Values are never read or set. |
| `profile` | A returning visitor: Camoufox loads the site once with a new profile, the profile is saved, and a second session with it loads the site again. |
| `LAB_SITES_HUMAN` | The three URLs come from the environment, so the published programs don't name the sites; set your own to repeat the measurement. |

## How it works

1.  For each site, take one sticky residential exit and, one session at a time, open each of the twelve setups, load the page once, wait 8 s, and read the page and HUMAN's cookies.
2.  Classify what the page shows and record the HTTP status of its first response; a network error gets one retry.
3.  Then load the site with Camoufox and a new profile, wait for the profile to be saved, load it again with the same profile and record the second visit. Delete the profile.

## The code

The same program in five languages (also [on GitHub](https://github.com/cdpfleet/cdpfleet/tree/main/cases/75-human-real-sites-by-browser), with the raw output). Set these environment variables first:

-   `CDPFLEET_API_KEY` — your API key (dashboard → API keys)

 

```node
// npm install playwright@1.60.0
// env: CDPFLEET_API_KEY, LAB_SITES_HUMAN (comma-separated URLs of the pages to test; one load per page per setup, nothing clicked)
import { randomBytes } from 'node:crypto';
import { chromium, firefox, webkit } from 'playwright';

const KEY = process.env.CDPFLEET_API_KEY;
const RUN = randomBytes(3).toString('hex');
const SITES = process.env.LAB_SITES_HUMAN.split(',').map((url, i) => ({ site: String.fromCharCode(65 + i), url }));
// Every browser the fleet offers that a Playwright 1.60 client can drive (Rebrowser needs 1.52, so it's left out).
const SETUPS = [
  ['chrome_headful', 'Chrome, headful', 'chrome', chromium, false],
  ['chrome_headless', 'Chrome, headless', 'chrome', chromium, 'new'],
  ['edge', 'Edge, headful', 'edge', chromium, false],
  ['brave', 'Brave, headful', 'brave', chromium, false],
  ['opera', 'Opera, headful', 'opera', chromium, false],
  ['yandex', 'Yandex, headful', 'yandex', chromium, false],
  ['whale', 'Whale, headful', 'whale', chromium, false],
  ['chromium_headless', 'Chromium, headless', 'chromium', chromium, true],
  ['patchright', 'Patchright, headful', 'patchright', chromium, false],
  ['firefox', 'Firefox, headful', 'firefox', firefox, false],
  ['webkit', 'WebKit, headful', 'webkit', webkit, false],
  ['camoufox', 'Camoufox, headful', 'camoufox', firefox, false],
];

// What the page shows after it loaded and had 8 s to run its checks, in the lab's words. HUMAN's
// challenge is its "Press & Hold" widget (#px-captcha), usually on a page titled "Access to this page
// has been denied" — interactive, so it counts as a challenge, not a block.
const STATE = `(() => {
  const t = (document.title + ' ' + (document.body ? document.body.innerText : '').slice(0, 600)).replace(/\\s+/g, ' ');
  const len = document.body ? document.body.innerText.length : 0;
  if (document.querySelector('#px-captcha') || (/press (&|and) hold|human touch|verify you are (a )?human|are you a robot|before we continue/i.test(t) && len < 4000)) return 'challenge';
  if (/access (to this page has been )?denied|has been blocked|you don.t have permission to access|request unsuccessful|reference #/i.test(t) && len < 4000) return 'blocked';
  return 'content';
})()`;

// HUMAN's cookies (_px3, _pxhd, _pxvid, pxcts …): which ones the site set, by name only — values are never read.
const pxCookies = (cookies) => cookies.map((c) => c.name).filter((n) => /^_?px/i.test(n)).sort();

async function launch(engine, headless, proxy, profile) {
  for (let attempt = 1; ; attempt++) {
    const res = await fetch(`https://starter.cdpfleet.com/${engine}/session`, {
      method: 'POST',
      headers: { 'x-api-key': KEY, 'content-type': 'application/json' },
      body: JSON.stringify({ proxy, headless, record: false, ...(profile ? { profile } : {}) }),
    });
    if (res.ok) return (await res.json()).wsUrl;
    if (![429, 503].includes(res.status) || attempt === 5) throw new Error(`launch ${res.status} ${await res.text()}`);
    await new Promise((r) => setTimeout(r, (Number(res.headers.get('retry-after')) || 5) * 1000));
  }
}

// One session, one load: the status of the page's own response, then the page's state and HUMAN's
// cookies 8 s later.
async function visit(setup, url, proxy, profile) {
  const [, , engine, type, headless] = setup;
  const browser = await type.connect(await launch(engine, headless, proxy, profile), { headers: { 'x-api-key': KEY } });
  try {
    const context = profile ? browser.contexts()[0] : await browser.newContext();
    const tab = await context.newPage();
    let status = 0;
    try {
      status = (await tab.goto(url, { waitUntil: 'domcontentloaded', timeout: 45000 }))?.status() ?? 0;
    } catch (err) {
      // A reset HTTP/2 connection is a refusal, not a network failure.
      return { outcome: /ERR_HTTP2_PROTOCOL_ERROR/.test(err.message) ? 'blocked' : 'error', status: 0, px_cookies: [] };
    }
    await tab.waitForTimeout(8000);
    let outcome = await tab.evaluate(STATE).catch(() => 'error');
    if (outcome === 'content' && status >= 400) outcome = 'blocked';
    return { outcome, status, px_cookies: pxCookies(await context.cookies()) };
  } finally {
    await browser.close();
  }
}

async function api(method, path) {
  const res = await fetch(`https://cdpfleet.com${path}`, { method, headers: { 'x-api-key': KEY } });
  return res.json().catch(() => ({}));
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// One sticky residential exit per site; its setups run one after another (one session at a time per
// site), and the sites run side by side. A network error gets one more try.
async function site({ site, url }) {
  const proxy = `cdpfleet-resi-country-us-session-c75${RUN}${site}-lifetime-30`;
  const results = {};
  for (const setup of SETUPS) {
    let r = await visit(setup, url, proxy).catch(() => ({ outcome: 'error', status: 0, px_cookies: [] }));
    if (r.outcome === 'error') r = await visit(setup, url, proxy).catch(() => ({ outcome: 'error', status: 0, px_cookies: [] }));
    results[setup[0]] = r;
  }
  // A returning visitor: Camoufox with a profile, a first visit and then a second session.
  const name = `case75-${RUN}-${site}`;
  const camoufox = SETUPS.find((s) => s[0] === 'camoufox');
  await visit(camoufox, url, proxy, name).catch(() => null);
  for (let j = 0; j < 60 && (await api('GET', '/v1/me/profiles')).profiles?.find((p) => p.name === name)?.in_use !== false; j++) await sleep(2000);
  results.camoufox_returning = await visit(camoufox, url, proxy, name).catch(() => ({ outcome: 'error', status: 0, px_cookies: [] }));
  await api('DELETE', `/v1/me/profiles/${name}`);
  return { site, results };
}

const sites = await Promise.all(SITES.map(site));
console.log(JSON.stringify({ measured_at: new Date().toISOString().slice(0, 10), setups: Object.fromEntries([...SETUPS.map((s) => [s[0], s[1]]), ['camoufox_returning', 'Camoufox, returning profile (2nd visit)']]), sites }, null, 2));
```

```python
# pip install playwright==1.60.0 requests
# env: CDPFLEET_API_KEY, LAB_SITES_HUMAN (comma-separated URLs of the pages to test; one load per page per setup, nothing clicked)
import asyncio
import json
import os
import secrets
from datetime import datetime, timezone

import requests
from playwright.async_api import async_playwright

KEY = os.environ["CDPFLEET_API_KEY"]
RUN = secrets.token_hex(3)
SITES = [{"site": chr(65 + i), "url": u} for i, u in enumerate(os.environ["LAB_SITES_HUMAN"].split(","))]
# Every browser the fleet offers that a Playwright 1.60 client can drive (Rebrowser needs 1.52, so it's left out).
SETUPS = [
    ("chrome_headful", "Chrome, headful", "chrome", "chromium", False),
    ("chrome_headless", "Chrome, headless", "chrome", "chromium", "new"),
    ("edge", "Edge, headful", "edge", "chromium", False),
    ("brave", "Brave, headful", "brave", "chromium", False),
    ("opera", "Opera, headful", "opera", "chromium", False),
    ("yandex", "Yandex, headful", "yandex", "chromium", False),
    ("whale", "Whale, headful", "whale", "chromium", False),
    ("chromium_headless", "Chromium, headless", "chromium", "chromium", True),
    ("patchright", "Patchright, headful", "patchright", "chromium", False),
    ("firefox", "Firefox, headful", "firefox", "firefox", False),
    ("webkit", "WebKit, headful", "webkit", "webkit", False),
    ("camoufox", "Camoufox, headful", "camoufox", "firefox", False),
]

# What the page shows after it loaded and had 8 s to run its checks, in the lab's words. HUMAN's
# challenge is its "Press & Hold" widget (#px-captcha), usually on a page titled "Access to this page
# has been denied" — interactive, so it counts as a challenge, not a block.
STATE = r"""(() => {
  const t = (document.title + ' ' + (document.body ? document.body.innerText : '').slice(0, 600)).replace(/\s+/g, ' ');
  const len = document.body ? document.body.innerText.length : 0;
  if (document.querySelector('#px-captcha') || (/press (&|and) hold|human touch|verify you are (a )?human|are you a robot|before we continue/i.test(t) && len < 4000)) return 'challenge';
  if (/access (to this page has been )?denied|has been blocked|you don.t have permission to access|request unsuccessful|reference #/i.test(t) && len < 4000) return 'blocked';
  return 'content';
})()"""

NONE = {"outcome": "error", "status": 0, "px_cookies": []}

# HUMAN's cookies (_px3, _pxhd, _pxvid, pxcts …): which ones the site set, by name only — values are never read.
def px_cookies(cookies):
    return sorted(c["name"] for c in cookies if c["name"].lower().lstrip("_").startswith("px"))

def api(method, path):
    try:
        return requests.request(method, f"https://cdpfleet.com{path}", headers={"x-api-key": KEY}, timeout=30).json()
    except Exception:
        return {}

async def launch(engine, headless, proxy, profile=None):
    body = {"proxy": proxy, "headless": headless, "record": False, **({"profile": profile} if profile else {})}
    for attempt in range(1, 6):
        res = await asyncio.to_thread(requests.post, f"https://starter.cdpfleet.com/{engine}/session",
                                      headers={"x-api-key": KEY}, json=body, timeout=120)
        if res.ok:
            return res.json()["wsUrl"]
        if res.status_code not in (429, 503) or attempt == 5:
            raise RuntimeError(f"launch {res.status_code} {res.text}")
        await asyncio.sleep(int(res.headers.get("retry-after") or 5))

# One session, one load: the status of the page's own response, then the page's state and HUMAN's
# cookies 8 s later.
async def visit(pw, setup, url, proxy, profile=None):
    _, _, engine, kind, headless = setup
    browser = await getattr(pw, kind).connect(await launch(engine, headless, proxy, profile), headers={"x-api-key": KEY})
    try:
        context = browser.contexts[0] if profile else await browser.new_context()
        tab = await context.new_page()
        try:
            res = await tab.goto(url, wait_until="domcontentloaded", timeout=45000)
            status = res.status if res else 0
        except Exception as err:
            # A reset HTTP/2 connection is a refusal, not a network failure.
            return {**NONE, "outcome": "blocked" if "ERR_HTTP2_PROTOCOL_ERROR" in str(err) else "error"}
        await tab.wait_for_timeout(8000)
        try:
            outcome = await tab.evaluate(STATE)
        except Exception:
            outcome = "error"
        if outcome == "content" and status >= 400:
            outcome = "blocked"
        return {"outcome": outcome, "status": status, "px_cookies": px_cookies(await context.cookies())}
    finally:
        await browser.close()

async def safe(pw, setup, url, proxy, profile=None):
    try:
        return await visit(pw, setup, url, proxy, profile)
    except Exception:
        return dict(NONE)

# One sticky residential exit per site; its setups run one after another (one session at a time per
# site), and the sites run side by side. A network error gets one more try.
async def site(pw, s):
    proxy = f"cdpfleet-resi-country-us-session-c75{RUN}{s['site']}-lifetime-30"
    results = {}
    for setup in SETUPS:
        r = await safe(pw, setup, s["url"], proxy)
        if r["outcome"] == "error":
            r = await safe(pw, setup, s["url"], proxy)
        results[setup[0]] = r
    # A returning visitor: Camoufox with a profile, a first visit and then a second session.
    name = f"case75-{RUN}-{s['site']}"
    camoufox = next(x for x in SETUPS if x[0] == "camoufox")
    await safe(pw, camoufox, s["url"], proxy, name)
    for _ in range(60):
        p = next((p for p in api("GET", "/v1/me/profiles").get("profiles", []) if p["name"] == name), None)
        if p and p.get("in_use") is False:
            break
        await asyncio.sleep(2)
    results["camoufox_returning"] = await safe(pw, camoufox, s["url"], proxy, name)
    api("DELETE", f"/v1/me/profiles/{name}")
    return {"site": s["site"], "results": results}

async def main():
    async with async_playwright() as pw:
        sites = await asyncio.gather(*(site(pw, s) for s in SITES))
    setups = {x[0]: x[1] for x in SETUPS}
    setups["camoufox_returning"] = "Camoufox, returning profile (2nd visit)"
    print(json.dumps({"measured_at": datetime.now(timezone.utc).date().isoformat(), "setups": setups, "sites": sites}, indent=2))

asyncio.run(main())
```

```java
// Maven: com.microsoft.playwright:playwright:1.60.0, com.google.code.gson:gson:2.11.0
// Run with PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1.
// env: CDPFLEET_API_KEY, LAB_SITES_HUMAN (comma-separated URLs of the pages to test; one load per page per setup, nothing clicked)
import com.google.gson.*;
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.WaitUntilState;
import java.net.URI;
import java.net.http.*;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.*;

public class Main {
  static final String KEY = System.getenv("CDPFLEET_API_KEY");
  static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().serializeNulls().create();
  static final HttpClient HTTP = HttpClient.newHttpClient();
  static final String RUN = String.format("%06x", new SecureRandom().nextInt(1 << 24));

  record Setup(String id, String name, String engine, String type, Object headless) {}

  // Every browser the fleet offers that a Playwright 1.60 client can drive (Rebrowser needs 1.52, so it's left out).
  static final List<Setup> SETUPS = List.of(
      new Setup("chrome_headful", "Chrome, headful", "chrome", "chromium", false),
      new Setup("chrome_headless", "Chrome, headless", "chrome", "chromium", "new"),
      new Setup("edge", "Edge, headful", "edge", "chromium", false),
      new Setup("brave", "Brave, headful", "brave", "chromium", false),
      new Setup("opera", "Opera, headful", "opera", "chromium", false),
      new Setup("yandex", "Yandex, headful", "yandex", "chromium", false),
      new Setup("whale", "Whale, headful", "whale", "chromium", false),
      new Setup("chromium_headless", "Chromium, headless", "chromium", "chromium", true),
      new Setup("patchright", "Patchright, headful", "patchright", "chromium", false),
      new Setup("firefox", "Firefox, headful", "firefox", "firefox", false),
      new Setup("webkit", "WebKit, headful", "webkit", "webkit", false),
      new Setup("camoufox", "Camoufox, headful", "camoufox", "firefox", false));

  // What the page shows after it loaded and had 8 s to run its checks, in the lab's words. HUMAN's
  // challenge is its "Press & Hold" widget (#px-captcha), usually on a page titled "Access to this page
  // has been denied" — interactive, so it counts as a challenge, not a block.
  static final String STATE = """
      (() => {
        const t = (document.title + ' ' + (document.body ? document.body.innerText : '').slice(0, 600)).replace(/\\s+/g, ' ');
        const len = document.body ? document.body.innerText.length : 0;
        if (document.querySelector('#px-captcha') || (/press (&|and) hold|human touch|verify you are (a )?human|are you a robot|before we continue/i.test(t) && len < 4000)) return 'challenge';
        if (/access (to this page has been )?denied|has been blocked|you don.t have permission to access|request unsuccessful|reference #/i.test(t) && len < 4000) return 'blocked';
        return 'content';
      })()""";

  static JsonObject api(String method, String path) {
    try {
      HttpRequest req = HttpRequest.newBuilder(URI.create("https://cdpfleet.com" + path)).header("x-api-key", KEY)
          .method(method, HttpRequest.BodyPublishers.noBody()).build();
      return JsonParser.parseString(HTTP.send(req, HttpResponse.BodyHandlers.ofString()).body()).getAsJsonObject();
    } catch (Exception e) {
      return new JsonObject();
    }
  }

  static String launch(String engine, Object headless, String proxy, String profile) throws Exception {
    Map<String, Object> body = new LinkedHashMap<>(Map.of("proxy", proxy, "headless", headless, "record", false));
    if (profile != null) body.put("profile", profile);
    for (int attempt = 1; ; attempt++) {
      HttpRequest req = HttpRequest.newBuilder(URI.create("https://starter.cdpfleet.com/" + engine + "/session"))
          .header("x-api-key", KEY).header("content-type", "application/json")
          .POST(HttpRequest.BodyPublishers.ofString(GSON.toJson(body))).build();
      HttpResponse<String> res = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
      if (res.statusCode() == 200) return JsonParser.parseString(res.body()).getAsJsonObject().get("wsUrl").getAsString();
      if ((res.statusCode() != 429 && res.statusCode() != 503) || attempt == 5) throw new RuntimeException("launch " + res.statusCode() + " " + res.body());
      Thread.sleep(Long.parseLong(res.headers().firstValue("retry-after").orElse("5")) * 1000);
    }
  }

  static Map<String, Object> cell(String outcome, int status, List<String> pxCookies) {
    Map<String, Object> m = new LinkedHashMap<>();
    m.put("outcome", outcome);
    m.put("status", status);
    m.put("px_cookies", pxCookies);
    return m;
  }

  // HUMAN's cookies (_px3, _pxhd, _pxvid, pxcts …): which ones the site set, by name only — values are never read.
  static List<String> pxCookies(BrowserContext context) {
    List<String> names = new ArrayList<>();
    for (com.microsoft.playwright.options.Cookie c : context.cookies()) {
      if (c.name.replaceFirst("^_", "").toLowerCase().startsWith("px")) names.add(c.name);
    }
    Collections.sort(names);
    return names;
  }

  // One session, one load: the status of the page's own response, then the page's state and HUMAN's
  // cookies 8 s later.
  static Map<String, Object> visit(Playwright pw, Setup s, String url, String proxy, String profile) {
    try {
      BrowserType type = switch (s.type()) { case "firefox" -> pw.firefox(); case "webkit" -> pw.webkit(); default -> pw.chromium(); };
      Browser browser = type.connect(launch(s.engine(), s.headless(), proxy, profile),
          new BrowserType.ConnectOptions().setHeaders(Map.of("x-api-key", KEY)));
      try {
        BrowserContext context = profile != null ? browser.contexts().get(0) : browser.newContext();
        Page tab = context.newPage();
        int status;
        try {
          Response res = tab.navigate(url, new Page.NavigateOptions().setWaitUntil(WaitUntilState.DOMCONTENTLOADED).setTimeout(45000));
          status = res != null ? res.status() : 0;
        } catch (PlaywrightException e) {
          // A reset HTTP/2 connection is a refusal, not a network failure.
          return cell(String.valueOf(e.getMessage()).contains("ERR_HTTP2_PROTOCOL_ERROR") ? "blocked" : "error", 0, List.of());
        }
        tab.waitForTimeout(8000);
        String outcome;
        try { outcome = (String) tab.evaluate(STATE); } catch (PlaywrightException e) { outcome = "error"; }
        if ("content".equals(outcome) && status >= 400) outcome = "blocked";
        return cell(outcome, status, pxCookies(context));
      } finally {
        browser.close();
      }
    } catch (Exception e) {
      return cell("error", 0, List.of());
    }
  }

  // One sticky residential exit per site; its setups run one after another (one session at a time per
  // site), and the sites run side by side. A network error gets one more try.
  static Map<String, Object> site(String label, String url) throws Exception {
    try (Playwright pw = Playwright.create()) {
      String proxy = "cdpfleet-resi-country-us-session-c75" + RUN + label + "-lifetime-30";
      Map<String, Object> results = new LinkedHashMap<>();
      for (Setup s : SETUPS) {
        Map<String, Object> r = visit(pw, s, url, proxy, null);
        if ("error".equals(r.get("outcome"))) r = visit(pw, s, url, proxy, null);
        results.put(s.id(), r);
      }
      // A returning visitor: Camoufox with a profile, a first visit and then a second session.
      String name = "case75-" + RUN + "-" + label;
      Setup camoufox = SETUPS.get(SETUPS.size() - 1);
      visit(pw, camoufox, url, proxy, name);
      for (int i = 0; i < 60; i++) {
        boolean free = false;
        JsonObject list = api("GET", "/v1/me/profiles");
        if (list.has("profiles")) for (JsonElement p : list.getAsJsonArray("profiles")) {
          JsonObject o = p.getAsJsonObject();
          if (name.equals(o.get("name").getAsString()) && o.has("in_use") && !o.get("in_use").getAsBoolean()) free = true;
        }
        if (free) break;
        Thread.sleep(2000);
      }
      results.put("camoufox_returning", visit(pw, camoufox, url, proxy, name));
      api("DELETE", "/v1/me/profiles/" + name);
      return new LinkedHashMap<>(Map.of("site", label, "results", results));
    }
  }

  public static void main(String[] args) throws Exception {
    String[] urls = System.getenv("LAB_SITES_HUMAN").split(",");
    ExecutorService pool = Executors.newFixedThreadPool(urls.length);
    List<Future<Map<String, Object>>> futures = new ArrayList<>();
    for (int i = 0; i < urls.length; i++) {
      String label = String.valueOf((char) ('A' + i)), url = urls[i];
      futures.add(pool.submit(() -> site(label, url)));
    }
    List<Map<String, Object>> sites = new ArrayList<>();
    for (Future<Map<String, Object>> f : futures) sites.add(f.get());
    pool.shutdown();
    Map<String, String> names = new LinkedHashMap<>();
    for (Setup s : SETUPS) names.put(s.id(), s.name());
    names.put("camoufox_returning", "Camoufox, returning profile (2nd visit)");
    Map<String, Object> out = new LinkedHashMap<>();
    out.put("measured_at", Instant.now().toString().substring(0, 10));
    out.put("setups", names);
    out.put("sites", sites);
    System.out.println(GSON.toJson(out));
  }
}
```

```csharp
// dotnet add package Microsoft.Playwright --version 1.60.0
// env: CDPFLEET_API_KEY, LAB_SITES_HUMAN (comma-separated URLs of the pages to test; one load per page per setup, nothing clicked)
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Playwright;

var key = Environment.GetEnvironmentVariable("CDPFLEET_API_KEY")!;
var run = Convert.ToHexString(RandomNumberGenerator.GetBytes(3)).ToLowerInvariant();
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", key);
using var playwright = await Playwright.CreateAsync();
var sites = Environment.GetEnvironmentVariable("LAB_SITES_HUMAN")!.Split(',').Select((u, i) => (Site: ((char)('A' + i)).ToString(), Url: u)).ToArray();

// Every browser the fleet offers that a Playwright 1.60 client can drive (Rebrowser needs 1.52, so it's left out).
var setups = new (string Id, string Name, string Engine, IBrowserType Type, JsonNode Headless)[]
{
    ("chrome_headful", "Chrome, headful", "chrome", playwright.Chromium, false),
    ("chrome_headless", "Chrome, headless", "chrome", playwright.Chromium, "new"),
    ("edge", "Edge, headful", "edge", playwright.Chromium, false),
    ("brave", "Brave, headful", "brave", playwright.Chromium, false),
    ("opera", "Opera, headful", "opera", playwright.Chromium, false),
    ("yandex", "Yandex, headful", "yandex", playwright.Chromium, false),
    ("whale", "Whale, headful", "whale", playwright.Chromium, false),
    ("chromium_headless", "Chromium, headless", "chromium", playwright.Chromium, true),
    ("patchright", "Patchright, headful", "patchright", playwright.Chromium, false),
    ("firefox", "Firefox, headful", "firefox", playwright.Firefox, false),
    ("webkit", "WebKit, headful", "webkit", playwright.Webkit, false),
    ("camoufox", "Camoufox, headful", "camoufox", playwright.Firefox, false),
};

// What the page shows after it loaded and had 8 s to run its checks, in the lab's words. HUMAN's
// challenge is its "Press & Hold" widget (#px-captcha), usually on a page titled "Access to this page
// has been denied" — interactive, so it counts as a challenge, not a block.
const string State = """
(() => {
  const t = (document.title + ' ' + (document.body ? document.body.innerText : '').slice(0, 600)).replace(/\s+/g, ' ');
  const len = document.body ? document.body.innerText.length : 0;
  if (document.querySelector('#px-captcha') || (/press (&|and) hold|human touch|verify you are (a )?human|are you a robot|before we continue/i.test(t) && len < 4000)) return 'challenge';
  if (/access (to this page has been )?denied|has been blocked|you don.t have permission to access|request unsuccessful|reference #/i.test(t) && len < 4000) return 'blocked';
  return 'content';
})()
""";

async Task<string> Launch(string engine, JsonNode headless, string proxy, string? profile)
{
    var body = new JsonObject { ["proxy"] = proxy, ["headless"] = headless.DeepClone(), ["record"] = false };
    if (profile != null) body["profile"] = profile;
    for (var attempt = 1; ; attempt++)
    {
        var res = await http.PostAsync($"https://starter.cdpfleet.com/{engine}/session", JsonContent.Create(body));
        if (res.IsSuccessStatusCode) return (await res.Content.ReadFromJsonAsync<JsonObject>())!["wsUrl"]!.GetValue<string>();
        if (((int)res.StatusCode != 429 && (int)res.StatusCode != 503) || attempt == 5)
            throw new Exception($"launch {(int)res.StatusCode} {await res.Content.ReadAsStringAsync()}");
        await Task.Delay((int)(res.Headers.RetryAfter?.Delta?.TotalSeconds ?? 5) * 1000);
    }
}

JsonObject Cell(string outcome, int status, IEnumerable<string>? pxCookies = null) =>
    new() { ["outcome"] = outcome, ["status"] = status, ["px_cookies"] = new JsonArray((pxCookies ?? []).Select(n => (JsonNode)n!).ToArray()) };

// HUMAN's cookies (_px3, _pxhd, _pxvid, pxcts …): which ones the site set, by name only — values are never read.
async Task<List<string>> PxCookies(IBrowserContext context) =>
    (await context.CookiesAsync()).Select(c => c.Name).Where(n => n.TrimStart('_').StartsWith("px", StringComparison.OrdinalIgnoreCase)).OrderBy(n => n, StringComparer.Ordinal).ToList();

// One session, one load: the status of the page's own response, then the page's state and HUMAN's
// cookies 8 s later.
async Task<JsonObject> Visit((string Id, string Name, string Engine, IBrowserType Type, JsonNode Headless) s, string url, string proxy, string? profile = null)
{
    try
    {
        var browser = await s.Type.ConnectAsync(await Launch(s.Engine, s.Headless, proxy, profile),
            new() { Headers = new Dictionary<string, string> { ["x-api-key"] = key } });
        try
        {
            var context = profile != null ? browser.Contexts[0] : await browser.NewContextAsync();
            var tab = await context.NewPageAsync();
            int status;
            try
            {
                var res = await tab.GotoAsync(url, new() { WaitUntil = WaitUntilState.DOMContentLoaded, Timeout = 45000 });
                status = res?.Status ?? 0;
            }
            catch (PlaywrightException e)
            {
                // A reset HTTP/2 connection is a refusal, not a network failure.
                return Cell(e.Message.Contains("ERR_HTTP2_PROTOCOL_ERROR") ? "blocked" : "error", 0);
            }
            await tab.WaitForTimeoutAsync(8000);
            string outcome;
            try { outcome = await tab.EvaluateAsync<string>(State); } catch (PlaywrightException) { outcome = "error"; }
            if (outcome == "content" && status >= 400) outcome = "blocked";
            return Cell(outcome, status, await PxCookies(context));
        }
        finally { await browser.CloseAsync(); }
    }
    catch (Exception) { return Cell("error", 0); }
}

// One sticky residential exit per site; its setups run one after another (one session at a time per
// site), and the sites run side by side. A network error gets one more try.
async Task<JsonObject> Site(string label, string url)
{
    var proxy = $"cdpfleet-resi-country-us-session-c75{run}{label}-lifetime-30";
    var results = new JsonObject();
    foreach (var s in setups)
    {
        var r = await Visit(s, url, proxy);
        if (r["outcome"]!.GetValue<string>() == "error") r = await Visit(s, url, proxy);
        results[s.Id] = r;
    }
    // A returning visitor: Camoufox with a profile, a first visit and then a second session.
    var name = $"case75-{run}-{label}";
    var camoufox = setups[^1];
    await Visit(camoufox, url, proxy, name);
    for (var i = 0; i < 60; i++)
    {
        var list = await http.GetFromJsonAsync<JsonObject>("https://cdpfleet.com/v1/me/profiles");
        var p = list?["profiles"]?.AsArray().FirstOrDefault(x => x?["name"]?.GetValue<string>() == name);
        if (p?["in_use"]?.GetValue<bool>() == false) break;
        await Task.Delay(2000);
    }
    results["camoufox_returning"] = await Visit(camoufox, url, proxy, name);
    await http.DeleteAsync($"https://cdpfleet.com/v1/me/profiles/{name}");
    return new JsonObject { ["site"] = label, ["results"] = results };
}

var done = await Task.WhenAll(sites.Select(x => Site(x.Site, x.Url)));
var names = new JsonObject();
foreach (var s in setups) names[s.Id] = s.Name;
names["camoufox_returning"] = "Camoufox, returning profile (2nd visit)";
var output = new JsonObject
{
    ["measured_at"] = DateTime.UtcNow.ToString("yyyy-MM-dd"),
    ["setups"] = names,
    ["sites"] = new JsonArray(done.Select(d => (JsonNode)d).ToArray()),
};
Console.WriteLine(output.ToJsonString(new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }));
```

```go
// go get github.com/playwright-community/playwright-go@v0.6000.0
// Driver: build playwright-core 1.60.0 from npm and set PLAYWRIGHT_DRIVER_PATH (see /docs/quickstart).
// env: CDPFLEET_API_KEY, LAB_SITES_HUMAN (comma-separated URLs of the pages to test; one load per page per setup, nothing clicked)
package main

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/playwright-community/playwright-go"
)

var key = os.Getenv("CDPFLEET_API_KEY")
var runID = func() string { b := make([]byte, 3); rand.Read(b); return hex.EncodeToString(b) }()

type setup struct {
	ID, Name, Engine, Type string
	Headless               any // false, true or "new"
}

// Every browser the fleet offers that a Playwright 1.60 client can drive (Rebrowser needs 1.52, so it's left out).
var setups = []setup{
	{"chrome_headful", "Chrome, headful", "chrome", "chromium", false},
	{"chrome_headless", "Chrome, headless", "chrome", "chromium", "new"},
	{"edge", "Edge, headful", "edge", "chromium", false},
	{"brave", "Brave, headful", "brave", "chromium", false},
	{"opera", "Opera, headful", "opera", "chromium", false},
	{"yandex", "Yandex, headful", "yandex", "chromium", false},
	{"whale", "Whale, headful", "whale", "chromium", false},
	{"chromium_headless", "Chromium, headless", "chromium", "chromium", true},
	{"patchright", "Patchright, headful", "patchright", "chromium", false},
	{"firefox", "Firefox, headful", "firefox", "firefox", false},
	{"webkit", "WebKit, headful", "webkit", "webkit", false},
	{"camoufox", "Camoufox, headful", "camoufox", "firefox", false},
}

// What the page shows after it loaded and had 8 s to run its checks, in the lab's words. HUMAN's
// challenge is its "Press & Hold" widget (#px-captcha), usually on a page titled "Access to this page
// has been denied" — interactive, so it counts as a challenge, not a block.
const state = `(() => {
  const t = (document.title + ' ' + (document.body ? document.body.innerText : '').slice(0, 600)).replace(/\s+/g, ' ');
  const len = document.body ? document.body.innerText.length : 0;
  if (document.querySelector('#px-captcha') || (/press (&|and) hold|human touch|verify you are (a )?human|are you a robot|before we continue/i.test(t) && len < 4000)) return 'challenge';
  if (/access (to this page has been )?denied|has been blocked|you don.t have permission to access|request unsuccessful|reference #/i.test(t) && len < 4000) return 'blocked';
  return 'content';
})()`

type cell struct {
	Outcome   string   `json:"outcome"`
	Status    int      `json:"status"`
	PxCookies []string `json:"px_cookies"`
}

func failed(outcome string) cell { return cell{Outcome: outcome, PxCookies: []string{}} }

// HUMAN's cookies (_px3, _pxhd, _pxvid, pxcts …): which ones the site set, by name only — values are never read.
func pxCookies(ctx playwright.BrowserContext) []string {
	names := []string{}
	cookies, err := ctx.Cookies()
	if err != nil {
		return names
	}
	for _, c := range cookies {
		if strings.HasPrefix(strings.ToLower(strings.TrimPrefix(c.Name, "_")), "px") {
			names = append(names, c.Name)
		}
	}
	sort.Strings(names)
	return names
}

func api(method, path string) map[string]any {
	req, _ := http.NewRequest(method, "https://cdpfleet.com"+path, nil)
	req.Header.Set("x-api-key", key)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return map[string]any{}
	}
	defer res.Body.Close()
	var out map[string]any
	json.NewDecoder(res.Body).Decode(&out)
	return out
}

func launch(engine string, headless any, proxy, profile string) (string, error) {
	body := map[string]any{"proxy": proxy, "headless": headless, "record": false}
	if profile != "" {
		body["profile"] = profile
	}
	b, _ := json.Marshal(body)
	for attempt := 1; ; attempt++ {
		req, _ := http.NewRequest("POST", "https://starter.cdpfleet.com/"+engine+"/session", bytes.NewReader(b))
		req.Header.Set("x-api-key", key)
		req.Header.Set("content-type", "application/json")
		res, err := http.DefaultClient.Do(req)
		if err != nil {
			return "", err
		}
		data, _ := io.ReadAll(res.Body)
		res.Body.Close()
		if res.StatusCode == 200 {
			var s struct{ WsUrl string }
			json.Unmarshal(data, &s)
			return s.WsUrl, nil
		}
		if (res.StatusCode != 429 && res.StatusCode != 503) || attempt == 5 {
			return "", fmt.Errorf("launch %d %s", res.StatusCode, data)
		}
		wait, _ := strconv.Atoi(res.Header.Get("retry-after"))
		if wait == 0 {
			wait = 5
		}
		time.Sleep(time.Duration(wait) * time.Second)
	}
}

// One session, one load: the status of the page's own response, then the page's state and HUMAN's
// cookies 8 s later.
func visit(pw *playwright.Playwright, s setup, url, proxy, profile string) cell {
	ws, err := launch(s.Engine, s.Headless, proxy, profile)
	if err != nil {
		return failed("error")
	}
	bt := map[string]playwright.BrowserType{"chromium": pw.Chromium, "firefox": pw.Firefox, "webkit": pw.WebKit}[s.Type]
	browser, err := bt.Connect(ws, playwright.BrowserTypeConnectOptions{Headers: map[string]string{"x-api-key": key}})
	if err != nil {
		return failed("error")
	}
	defer browser.Close()
	var ctx playwright.BrowserContext
	if profile != "" {
		ctx = browser.Contexts()[0]
	} else if ctx, err = browser.NewContext(); err != nil {
		return failed("error")
	}
	tab, err := ctx.NewPage()
	if err != nil {
		return failed("error")
	}
	res, err := tab.Goto(url, playwright.PageGotoOptions{WaitUntil: playwright.WaitUntilStateDomcontentloaded, Timeout: playwright.Float(45000)})
	if err != nil {
		// A reset HTTP/2 connection is a refusal, not a network failure.
		if strings.Contains(err.Error(), "ERR_HTTP2_PROTOCOL_ERROR") {
			return failed("blocked")
		}
		return failed("error")
	}
	status := 0
	if res != nil {
		status = res.Status()
	}
	tab.WaitForTimeout(8000)
	v, err := tab.Evaluate(state)
	outcome, _ := v.(string)
	if err != nil || outcome == "" {
		outcome = "error"
	}
	if outcome == "content" && status >= 400 {
		outcome = "blocked"
	}
	return cell{outcome, status, pxCookies(ctx)}
}

// One sticky residential exit per site; its setups run one after another (one session at a time per
// site), and the sites run side by side. A network error gets one more try.
func site(label, url string) map[string]any {
	pw, err := playwright.Run()
	if err != nil {
		log.Fatal(err)
	}
	defer pw.Stop()
	proxy := "cdpfleet-resi-country-us-session-c75" + runID + label + "-lifetime-30"
	results := map[string]cell{}
	for _, s := range setups {
		r := visit(pw, s, url, proxy, "")
		if r.Outcome == "error" {
			r = visit(pw, s, url, proxy, "")
		}
		results[s.ID] = r
	}
	// A returning visitor: Camoufox with a profile, a first visit and then a second session.
	name := "case75-" + runID + "-" + label
	camoufox := setups[len(setups)-1]
	visit(pw, camoufox, url, proxy, name)
	for i := 0; i < 60; i++ {
		free := false
		if list, ok := api("GET", "/v1/me/profiles")["profiles"].([]any); ok {
			for _, p := range list {
				if m := p.(map[string]any); m["name"] == name && m["in_use"] == false {
					free = true
				}
			}
		}
		if free {
			break
		}
		time.Sleep(2 * time.Second)
	}
	results["camoufox_returning"] = visit(pw, camoufox, url, proxy, name)
	api("DELETE", "/v1/me/profiles/"+name)
	return map[string]any{"site": label, "results": results}
}

func main() {
	urls := strings.Split(os.Getenv("LAB_SITES_HUMAN"), ",")
	sites := make([]map[string]any, len(urls))
	var wg sync.WaitGroup
	for i, u := range urls {
		wg.Add(1)
		go func(i int, u string) { defer wg.Done(); sites[i] = site(string(rune('A'+i)), u) }(i, u)
	}
	wg.Wait()
	names := map[string]string{"camoufox_returning": "Camoufox, returning profile (2nd visit)"}
	for _, s := range setups {
		names[s.ID] = s.Name
	}
	out, _ := json.MarshalIndent(map[string]any{"measured_at": time.Now().UTC().Format("2006-01-02"), "setups": names, "sites": sites}, "", "  ")
	fmt.Println(string(out))
}
```

## What we got

| Setup | Site A, a big-box retailer | Site B, a real-estate marketplace | Site C, a freelance marketplace | Measured |
| --- | --- | --- | --- | --- |
| Chrome, headful | content (200) | error | challenge (403) | 2026-10-29 |
| Chrome, headless | content (200) | challenge (403) | challenge (403) | 2026-10-29 |
| Edge, headful | content (200) | error | challenge (403) | 2026-10-29 |
| Brave, headful | content (200) | content (200) | challenge (403) | 2026-10-29 |
| Opera, headful | content (200) | content (200) | challenge (403) | 2026-10-29 |
| Yandex, headful | content (200) | content (200) | content (200) | 2026-10-29 |
| Whale, headful | content (200) | content (200) | content (200) | 2026-10-29 |
| Chromium, headless | content (200) | challenge (403) | challenge (403) | 2026-10-29 |
| Patchright, headful | content (200) | content (200) | challenge (403) | 2026-10-29 |
| Firefox, headful | challenge (200) | content (200) | content (200) | 2026-10-29 |
| WebKit, headful | content (200) | content (200) | content (200) | 2026-10-29 |
| Camoufox, headful | content (200) | content (200) | content (200) | 2026-10-29 |
| Camoufox, returning profile (2nd visit) | challenge (200) | content (200) | challenge (403) | 2026-10-29 |

From the Node.js run on 2026-10-29. IP addresses are replaced with placeholders (203.0.113.x); equal addresses stay equal. The other languages produced the same findings.

Raw output (Node.js)

```
{
  "measured_at": "2026-10-29",
  "setups": {
    "chrome_headful": "Chrome, headful",
    "chrome_headless": "Chrome, headless",
    "edge": "Edge, headful",
    "brave": "Brave, headful",
    "opera": "Opera, headful",
    "yandex": "Yandex, headful",
    "whale": "Whale, headful",
    "chromium_headless": "Chromium, headless",
    "patchright": "Patchright, headful",
    "firefox": "Firefox, headful",
    "webkit": "WebKit, headful",
    "camoufox": "Camoufox, headful",
    "camoufox_returning": "Camoufox, returning profile (2nd visit)"
  },
  "sites": [
    {
      "site": "A",
      "results": {
        "chrome_headful": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_pxhd"
          ]
        },
        "chrome_headless": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxvid",
            "pxcts"
          ]
        },
        "edge": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "brave": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "opera": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "yandex": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "whale": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "chromium_headless": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxvid",
            "pxcts"
          ]
        },
        "patchright": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "firefox": {
          "outcome": "challenge",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "webkit": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "camoufox": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "camoufox_returning": {
          "outcome": "challenge",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        }
      }
    },
    {
      "site": "B",
      "results": {
        "chrome_headful": {
          "outcome": "error",
          "status": 0,
          "px_cookies": []
        },
        "chrome_headless": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": []
        },
        "edge": {
          "outcome": "error",
          "status": 0,
          "px_cookies": []
        },
        "brave": {
          "outcome": "content",
          "status": 200,
          "px_cookies": []
        },
        "opera": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxvid",
            "pxcts"
          ]
        },
        "yandex": {
          "outcome": "content",
          "status": 200,
          "px_cookies": []
        },
        "whale": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxvid",
            "pxcts"
          ]
        },
        "chromium_headless": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": []
        },
        "patchright": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxvid",
            "pxcts"
          ]
        },
        "firefox": {
          "outcome": "content",
          "status": 200,
          "px_cookies": []
        },
        "webkit": {
          "outcome": "content",
          "status": 200,
          "px_cookies": []
        },
        "camoufox": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxvid",
            "pxcts"
          ]
        },
        "camoufox_returning": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxvid",
            "pxcts"
          ]
        }
      }
    },
    {
      "site": "C",
      "results": {
        "chrome_headful": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "chrome_headless": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "edge": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "brave": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "opera": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "yandex": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "whale": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "chromium_headless": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "patchright": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "firefox": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "webkit": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "camoufox": {
          "outcome": "content",
          "status": 200,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        },
        "camoufox_returning": {
          "outcome": "challenge",
          "status": 403,
          "px_cookies": [
            "_px3",
            "_pxde",
            "_pxhd",
            "_pxvid",
            "pxcts"
          ]
        }
      }
    }
  ]
}
```

## Takeaways

-   **HUMAN never blocked outright; every stop was its Press & Hold challenge.** On 2026-10-29, across five runs of 39 loads each, no load got a block page: a stopped visitor always got the interactive challenge, with HTTP 403 on sites B and C and an HTTP 200 challenge page on site A. The program clicks nothing, so a challenge is the end of the visit.
-   **A returning Camoufox profile made things worse.** Fresh Camoufox got the page in 13 of 15 loads (site A 4/5, B 5/5, C 4/5); the second visit with a saved profile was challenged in 14 of 15. That is the reverse of Akamai, where a returning profile turned 2/5 into 5/5 on the hardest site ([case 73](https://cdpfleet.com/docs/cases/akamai-real-sites-by-browser)), and of DataDome, where it changed nothing ([case 71](https://cdpfleet.com/docs/cases/datadome-real-sites-by-browser)): with HUMAN, cookies from an earlier visit counted against the visitor on these sites.
-   **Headless was challenged on both protected sites, and let in on the easy one.** Headless Chrome and headless Chromium got the challenge (403) on sites B and C in all five runs, and the page on site A in all five. Site A let every Chromium-based setup in on every run, except Opera once.
-   **On the hardest site, headful Chrome was stopped and WebKit got in.** Site C challenged headful Chrome, Brave and Patchright in every run, as it did both headless setups; WebKit got the page in all five runs, Camoufox and Whale in four, Firefox, Opera and Yandex in three, Edge in two. With DataDome, headful Chrome was the one setup let in everywhere ([case 71](https://cdpfleet.com/docs/cases/datadome-real-sites-by-browser)).
-   **Site B split the Chromium family.** In four of the five runs headful Chrome, Yandex, Whale and fresh Camoufox got the page every time, while Edge, Brave, Opera, Patchright and Firefox were challenged every time and WebKit half the time. In the fifth run (the Node.js one) the site let most setups in, and six of its loads got no HUMAN cookie at all — the same site can apply different rules from one hour to the next.
-   **HUMAN set its cookies whether it let you in or not.** `_pxvid` or `_px3` was there after 184 of 193 measured loads, and `_px3` after 86 of 89 challenges — as on HUMAN's own site ([case 67](https://cdpfleet.com/docs/cases/akamai-and-human-what-gets-through)), the cookie is no sign of having passed. Firefox, unlike Camoufox, was challenged on the easy site in four runs of five. The overview across vendors is [/bot-protection](https://cdpfleet.com/bot-protection).

---
Source: https://cdpfleet.com/docs/cases/human-real-sites-by-browser · Markdown: https://cdpfleet.com/docs/cases/human-real-sites-by-browser.md · All docs for AI tools: https://cdpfleet.com/llms.txt
