cdpfleet Docs GitHub Dashboard

Cases / #67 · 2026-10-23 · Medium

Akamai and HUMAN: what gets through? Four browsers, three networks, the vendors' own sites

akamai.com (Akamai Bot Manager) and humansecurity.com (HUMAN, formerly PerimeterX), each protected by its own vendor, loaded once per cell by headless Chromium, headful Chrome, headful Edge and Camoufox through your own proxy and sticky cdpfleet-resi exits (Tier 1 and Tier 4). The outcome per cell, the HTTP status, how many sensor posts the vendor's script sent, what Akamai's `_abck` cookie said before and after them, whether HUMAN issued `_px3`, and what a returning visitor with a profile sees.

Chromium Google Chrome Microsoft Edge Camoufox

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

The problem

Akamai Bot Manager and HUMAN both decide in two steps: the first response already depends on the request and the IP, and then a script in the page collects signals from the browser and posts them back (a "sensor"), which updates the cookie that later requests carry. A refusal shows as a 403 with an "Access Denied" page (Akamai) or a "Press & Hold" challenge (HUMAN). This case measures, on each vendor's own home page, which setup gets which outcome, and watches the cookies around the sensor posts. It is a dated measurement, not an evasion guide: nothing is clicked, held or solved, every cell is loaded once, and each vendor tunes its own site as it likes. For the signals a page can read, see What a page can read about your browser.

What we used, and why

WhatWhy
Four setupsHeadless Chromium, headful Google Chrome, headful Microsoft Edge (all Chromium) and Camoufox (based on Firefox source; not affiliated with Mozilla), launched with record: false.
proxy: your own vs cdpfleet-resi-country-us-session-<id>Your own proxy (PROXY_URL, rotating) against a sticky residential exit shared by the four setups.
cdpfleet-resi-tier-4-…The cheaper Tier 4 pool, with headful Chrome and Camoufox.
A fresh context per pageEvery load is a first visit: no cookie from one page or setup reaches the next.
page.on("request")Counts the POST requests the vendor's script sends: to akamai.com's own host for Akamai, to px-cloud.net / px-cdn.net for HUMAN.
context.cookies()Akamai's _abck carries a status between its first two ~: -1 until a sensor post is accepted. Only that field is recorded, at load and after 10 s, never the cookie itself. For HUMAN, whether _px3 was issued.
page.evaluate() once a secondClassifies the page: "Access Denied" = blocked, HUMAN's Press & Hold (#px-captcha) or an Akamai challenge = challenge, otherwise the real page. Nothing is clicked.
profileA returning visitor: the same profile and the same sticky exit in a second session, keeping the _abck the first visit was given.

How it works

  1. For each network (your own proxy, then a sticky Tier 1 exit) and each of the four setups, launch one session and load both home pages once each, in a fresh context.
  2. Record the HTTP status and the cookies, watch the page once a second for 10 s while the vendor's script posts its sensor data, then record the outcome and the cookies again.
  3. Load both pages on a sticky Tier 4 exit with headful Chrome and Camoufox.
  4. With headful Chrome, load akamai.com with a new profile, end the session, wait for the profile to be saved, and load it again with the same profile and exit. Delete the profile.

The code

The same program in five languages (also on GitHub, with the raw output). Set these environment variables first:

// npm install [email protected]
// env: CDPFLEET_API_KEY, PROXY_URL (one page load per page per setup and network; nothing is clicked or solved)
import { randomBytes } from 'node:crypto';
import { chromium, firefox } from 'playwright';

const KEY = process.env.CDPFLEET_API_KEY;
const RUN = randomBytes(3).toString('hex');
// Sticky residential exits: every setup in a comparison uses the same session name, so the same exit IP.
const NETWORKS = {
  own: { label: 'own proxy (rotating)', proxy: process.env.PROXY_URL },
  tier1: { label: 'cdpfleet-resi Tier 1, sticky US', proxy: `cdpfleet-resi-country-us-session-c67${RUN}-lifetime-30` },
  tier4: { label: 'cdpfleet-resi Tier 4, sticky US', proxy: `cdpfleet-resi-tier-4-country-us-session-c67${RUN}-lifetime-30` },
};
const SETUPS = {
  chromium: { setup: 'Chromium, headless', engine: 'chromium', type: chromium, headless: true },
  chrome: { setup: 'Google Chrome, headful', engine: 'chrome', type: chromium, headless: false },
  edge: { setup: 'Microsoft Edge, headful', engine: 'edge', type: chromium, headless: false },
  camoufox: { setup: 'Camoufox, defaults', engine: 'camoufox', type: firefox, headless: true },
};
// Each vendor's own home page, which that vendor protects with its own product.
const PAGES = {
  akamai: { url: 'https://www.akamai.com/', sensor: (u) => new URL(u).host === 'www.akamai.com' },
  human: { url: 'https://www.humansecurity.com/', sensor: (u) => /(^|\.)px-(cloud|cdn)\.net$/.test(new URL(u).host) },
};

// What the page shows right now, in the lab's words. The same expression runs in every language.
const STATE = `(() => {
  const t = (document.title + ' ' + (document.body ? document.body.innerText : '').slice(0, 400)).replace(/\\s+/g, ' ');
  const blocked = /access denied|you don't have permission to access|request blocked|reference #\\d/i.test(t);
  const challenge = !!document.querySelector('#px-captcha, #sec-if-cpt-container, #sec-cpt-if') || /press (&|and) hold|challenge validation/i.test(t);
  return blocked ? 'blocked' : challenge ? 'challenge' : 'page';
})()`;

// 503 means the fleet is momentarily busy: wait Retry-After and try again.
async function launch(s, network, profile) {
  for (let attempt = 1; ; attempt++) {
    const res = await fetch(`https://starter.cdpfleet.com/${s.engine}/session`, {
      method: 'POST',
      headers: { 'x-api-key': KEY, 'content-type': 'application/json' },
      body: JSON.stringify({ proxy: NETWORKS[network].proxy, headless: s.headless, record: false, ...(profile ? { profile } : {}) }),
    });
    if (res.ok) return s.type.connect((await res.json()).wsUrl, { headers: { 'x-api-key': KEY } });
    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));
  }
}

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// Akamai's _abck carries a status between its first two "~": -1 until a sensor post is accepted. Only that
// field is kept, never the cookie itself. HUMAN issues _px3 once its collector accepts the browser.
async function signals(context) {
  const jar = await context.cookies();
  const abck = jar.find((c) => c.name === '_abck')?.value;
  return { abck: abck ? abck.split('~')[1] ?? '?' : null, px3: jar.some((c) => c.name === '_px3') };
}

// One load, then watch the page once a second for 10 s without touching it, so the vendor's script
// can post its sensor data; a network failure is retried once.
async function visit(context, key) {
  const page = PAGES[key];
  const tab = await context.newPage();
  let posts = 0;
  tab.on('request', (r) => { try { if (r.method() === 'POST' && page.sensor(r.url())) posts++; } catch {} });
  let status = null;
  try {
    try {
      status = (await tab.goto(page.url, { waitUntil: 'domcontentloaded', timeout: 45000 }))?.status() ?? null;
    } catch {
      status = (await tab.goto(page.url, { waitUntil: 'domcontentloaded', timeout: 45000 }))?.status() ?? null;
    }
  } catch {
    await tab.close();
    return { outcome: 'error' };
  }
  const atLoad = await signals(context);
  let seen = new Set();
  for (let i = 0; i < 10; i++) {
    const s = await tab.evaluate(STATE).catch(() => null); // null while the page navigates
    if (s) seen.add(s);
    if (s === 'blocked') break;
    await sleep(1000);
  }
  const last = await tab.evaluate(STATE).catch(() => null);
  const outcome = seen.has('blocked') || last === 'blocked' ? 'blocked'
    : last === 'challenge' ? 'challenge' : seen.has('challenge') ? 'cleared' : 'content';
  const after = await signals(context);
  await tab.close();
  const cell = { outcome, status, sensor_posts: posts };
  if (key === 'akamai') Object.assign(cell, { abck_at_load: atLoad.abck, abck_after: after.abck });
  else cell.px3 = after.px3;
  return cell;
}

// One session per setup and network; a fresh context per page so every load is a first visit.
async function row(setupKey, network, pages) {
  const s = SETUPS[setupKey];
  const browser = await launch(s, network);
  const out = { setup: s.setup, network: NETWORKS[network].label };
  try {
    for (const key of pages) {
      const context = await browser.newContext();
      out[key] = await visit(context, key);
      await context.close();
    }
  } finally {
    await browser.close();
  }
  return out;
}

async function api(method, path) {
  const res = await fetch(`https://cdpfleet.com${path}`, { method, headers: { 'x-api-key': KEY } });
  return res.json().catch(() => ({}));
}
const profileInfo = async (name) => (await api('GET', '/v1/me/profiles')).profiles?.find((p) => p.name === name);

// A returning visitor: the same profile and the same sticky exit, two sessions a few seconds apart.
async function returning(setupKey, network, key) {
  const name = `case67-${RUN}`;
  const visits = [];
  for (let i = 0; i < 2; i++) {
    const browser = await launch(SETUPS[setupKey], network, name);
    try {
      visits.push(await visit(browser.contexts()[0], key)); // the profile's own context; a newContext() is not saved
    } finally {
      await browser.close();
    }
    // The node saves the profile after the browser exits: wait until it is free again.
    for (let j = 0; j < 60 && (await profileInfo(name))?.in_use !== false; j++) await sleep(2000);
  }
  await api('DELETE', `/v1/me/profiles/${name}`);
  return { setup: SETUPS[setupKey].setup, network: NETWORKS[network].label, page: key, first: visits[0], second: visits[1] };
}

const matrix = [];
for (const network of ['own', 'tier1']) {
  for (const setupKey of ['chromium', 'chrome', 'edge', 'camoufox']) matrix.push(await row(setupKey, network, Object.keys(PAGES)));
}
const tier4 = [];
for (const setupKey of ['chrome', 'camoufox']) tier4.push(await row(setupKey, 'tier4', Object.keys(PAGES)));
const profile = await returning('chrome', 'tier1', 'akamai');

console.log(JSON.stringify({ measured_at: new Date().toISOString().slice(0, 10), matrix, tier4, profile }, null, 2));

What we got

SetupNetworkakamai.com (Akamai)humansecurity.com (HUMAN)Measured
Chromium, headlessown proxy (rotating)blocked (HTTP 403)challenge (HTTP 403, _px3, 5 sensor posts)2026-10-23
Google Chrome, headfulown proxy (rotating)content (HTTP 200, _abck -1 → -1, 3 sensor posts)content (HTTP 200, _px3, 4 sensor posts)2026-10-23
Microsoft Edge, headfulown proxy (rotating)content (HTTP 200, _abck -1 → -1, 2 sensor posts)content (HTTP 200, _px3, 7 sensor posts)2026-10-23
Camoufox, defaultsown proxy (rotating)content (HTTP 200, _abck -1 → -1, 1 sensor posts)challenge (HTTP 403, _px3, 3 sensor posts)2026-10-23
Chromium, headlesscdpfleet-resi Tier 1, sticky USblocked (HTTP 403)challenge (HTTP 403, _px3, 5 sensor posts)2026-10-23
Google Chrome, headfulcdpfleet-resi Tier 1, sticky UScontent (HTTP 200, _abck -1 → -1, 3 sensor posts)content (HTTP 200, _px3, 7 sensor posts)2026-10-23
Microsoft Edge, headfulcdpfleet-resi Tier 1, sticky UScontent (HTTP 200, _abck -1 → -1, 3 sensor posts)challenge (HTTP 403, _px3, 5 sensor posts)2026-10-23
Camoufox, defaultscdpfleet-resi Tier 1, sticky UScontent (HTTP 200, _abck -1 → -1, 3 sensor posts)challenge (HTTP 403, _px3, 3 sensor posts)2026-10-23
Google Chrome, headfulcdpfleet-resi Tier 4, sticky UScontent (HTTP 200, _abck -1 → -1, 3 sensor posts)content (HTTP 200, _px3, 8 sensor posts)2026-10-23
Camoufox, defaultscdpfleet-resi Tier 4, sticky UScontent (HTTP 200, _abck -1 → -1, 3 sensor posts)challenge (HTTP 403, _px3, 3 sensor posts)2026-10-23
Google Chrome, headful, returning (profile)cdpfleet-resi Tier 1, sticky US1st visit: content (HTTP 200, _abck -1 → -1, 3 sensor posts) → 2nd: content (HTTP 200, _abck -1 → -1, 3 sensor posts)—2026-10-23

From the Node.js run on 2026-10-23. 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-23",
  "matrix": [
    {
      "setup": "Chromium, headless",
      "network": "own proxy (rotating)",
      "akamai": {
        "outcome": "blocked",
        "status": 403,
        "sensor_posts": 0,
        "abck_at_load": null,
        "abck_after": null
      },
      "human": {
        "outcome": "challenge",
        "status": 403,
        "sensor_posts": 5,
        "px3": true
      }
    },
    {
      "setup": "Google Chrome, headful",
      "network": "own proxy (rotating)",
      "akamai": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 3,
        "abck_at_load": "-1",
        "abck_after": "-1"
      },
      "human": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 4,
        "px3": true
      }
    },
    {
      "setup": "Microsoft Edge, headful",
      "network": "own proxy (rotating)",
      "akamai": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 2,
        "abck_at_load": "-1",
        "abck_after": "-1"
      },
      "human": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 7,
        "px3": true
      }
    },
    {
      "setup": "Camoufox, defaults",
      "network": "own proxy (rotating)",
      "akamai": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 1,
        "abck_at_load": "-1",
        "abck_after": "-1"
      },
      "human": {
        "outcome": "challenge",
        "status": 403,
        "sensor_posts": 3,
        "px3": true
      }
    },
    {
      "setup": "Chromium, headless",
      "network": "cdpfleet-resi Tier 1, sticky US",
      "akamai": {
        "outcome": "blocked",
        "status": 403,
        "sensor_posts": 0,
        "abck_at_load": null,
        "abck_after": null
      },
      "human": {
        "outcome": "challenge",
        "status": 403,
        "sensor_posts": 5,
        "px3": true
      }
    },
    {
      "setup": "Google Chrome, headful",
      "network": "cdpfleet-resi Tier 1, sticky US",
      "akamai": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 3,
        "abck_at_load": "-1",
        "abck_after": "-1"
      },
      "human": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 7,
        "px3": true
      }
    },
    {
      "setup": "Microsoft Edge, headful",
      "network": "cdpfleet-resi Tier 1, sticky US",
      "akamai": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 3,
        "abck_at_load": "-1",
        "abck_after": "-1"
      },
      "human": {
        "outcome": "challenge",
        "status": 403,
        "sensor_posts": 5,
        "px3": true
      }
    },
    {
      "setup": "Camoufox, defaults",
      "network": "cdpfleet-resi Tier 1, sticky US",
      "akamai": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 3,
        "abck_at_load": "-1",
        "abck_after": "-1"
      },
      "human": {
        "outcome": "challenge",
        "status": 403,
        "sensor_posts": 3,
        "px3": true
      }
    }
  ],
  "tier4": [
    {
      "setup": "Google Chrome, headful",
      "network": "cdpfleet-resi Tier 4, sticky US",
      "akamai": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 3,
        "abck_at_load": "-1",
        "abck_after": "-1"
      },
      "human": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 8,
        "px3": true
      }
    },
    {
      "setup": "Camoufox, defaults",
      "network": "cdpfleet-resi Tier 4, sticky US",
      "akamai": {
        "outcome": "content",
        "status": 200,
        "sensor_posts": 3,
        "abck_at_load": "-1",
        "abck_after": "-1"
      },
      "human": {
        "outcome": "challenge",
        "status": 403,
        "sensor_posts": 3,
        "px3": true
      }
    }
  ],
  "profile": {
    "setup": "Google Chrome, headful",
    "network": "cdpfleet-resi Tier 1, sticky US",
    "page": "akamai",
    "first": {
      "outcome": "content",
      "status": 200,
      "sensor_posts": 3,
      "abck_at_load": "-1",
      "abck_after": "-1"
    },
    "second": {
      "outcome": "content",
      "status": 200,
      "sensor_posts": 3,
      "abck_at_load": "-1",
      "abck_after": "-1"
    }
  }
}

Takeaways