Stealth Chromium that passes every bot detection test.
| Not a patched config. Not a JS injection. A real Chromium binary with fingerprints modified at the C++ source level. Antibot systems score it as a normal browser — because it is a normal browser. |

Cloudflare Turnstile — 3 live tests passing (headed mode, macOS)
Drop-in Playwright/Puppeteer replacement for Python and JavaScript.
Same API, same code — just swap the import. 3 lines of code, 30 seconds to unblock.
- 87 source-level C++ patches — canvas, WebGL, audio, fonts, GPU, screen, WebRTC, network timing, automation signals, CDP input behavior
humanize=True— human-like mouse curves, keyboard timing, and scroll patterns. One flag, behavioral detection passes- Pro: 0.9 reCAPTCHA v3 score — human-level, server-verified
- Passes Cloudflare Turnstile, FingerprintJS, BrowserScan — tested against 30+ detection sites
- Auto-downloads the right binary — free or Pro based on your license
pip install cloakbrowserornpm install cloakbrowser— binary auto-downloads, zero config- Latest binary, free to try — sign in with GitHub, point the newest build at your hardest target, scale to thousands of sessions on Pro
Try it now — no install needed:
docker run --rm cloakhq/cloakbrowser cloaktestPython:
from cloakbrowser import launch
browser = launch()
page = browser.new_page()
page.goto("https://example.com")
browser.close()JavaScript (Playwright):
import { launch } from 'cloakbrowser';
const browser = await launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();Also works with Puppeteer: import { launch } from 'cloakbrowser/puppeteer' (details)
For sites with anti-bot protection, add a residential proxy and these flags:
browser = launch(
proxy="http://user:pass@residential-proxy:port", # residential IP, not datacenter
geoip=True, # match timezone + locale to proxy IP
headless=False, # some sites detect headless even with C++ patches
humanize=True, # human-like mouse, keyboard, scroll
)const browser = await launch({
proxy: 'http://user:pass@residential-proxy:port',
geoip: true,
headless: false,
humanize: true,
});See Troubleshooting for site-specific issues (FingerprintJS, Kasada, reCAPTCHA).
Install
Python:
pip install cloakbrowserJavaScript / Node.js:
# With Playwright
npm install cloakbrowser playwright-core
# With Puppeteer
npm install cloakbrowser puppeteer-core.NET / C#:
dotnet add package CloakBrowserCommunity-maintained .NET client built on Microsoft.Playwright. See
dotnet/README.mdfor the full API.
On first run, the stealth Chromium binary is automatically downloaded (~200MB, cached locally).
Optional: Auto-detect timezone/locale from proxy IP:
pip install 'cloakbrowser[geoip]'Migrating from Playwright? One-line change:
- from playwright.sync_api import sync_playwright
- pw = sync_playwright().start()
- browser = pw.chromium.launch()
+ from cloakbrowser import launch
+ browser = launch()
page = browser.new_page()
page.goto("https://example.com")
# ... rest of your code works unchanged⭐ Star to show support — Watch releases to get notified when new builds drop.
Latest: v0.5.11 — 87 source-level stealth patches (Chromium 152.0.7977.82.1)
- CloakBrowser Pro Stable — Chromium
152.0.7977.82.1on Linux x64, Linux ARM64, and Windows x64; macOS on151.0.7922.108.3. Set alicense_key(licenseKeyin JS) or theCLOAKBROWSER_LICENSE_KEYenv var and the wrapper fetches the latest Stable build for your platform automatically. See CloakBrowser Pro - .NET 8 / C# client — CloakBrowser now ships as a NuGet package (
CloakBrowser), mirroring the Python and JS wrappers. - Chromium 152 upgrade — rebased the full patch set onto Chromium 152 for Linux and Windows; macOS stays on Chromium 151
- 87 fingerprint patches — rendering consistency improvements across Linux and Windows, corrected GPU/display/graphics parameters to match stock Chrome profiles
- Windows native GPU passthrough — real hardware values pass through directly instead of being spoofed, matching real browser behavior
- HTTP proxy inline credentials — new network-layer support for proxies with inline authentication
extension_paths— load Chrome extensions in all launch functions- Humanize actionability — auto-wait for visible, enabled, stable elements before humanized actions
- Per-call
human_config— override humanize settings on individual method calls - Composable JS helpers —
buildLaunchOptions()andhumanizeBrowser()for custom Playwright integrations - Native SOCKS5 proxy —
proxy="socks5://user:pass@host:port"works directly in all launch functions, Python + JS. QUIC/HTTP3 tunnels through SOCKS5 via UDP ASSOCIATE - Proxy signal removal — DNS/connect/SSL timing zeroed, proxy cache headers stripped, Proxy-Connection header leak removed
- Chromium 146 upgrade — rebased all patches from 145.0.7632.x to 146.0.7680.177
- WebRTC IP spoofing —
--fingerprint-webrtc-ip=autoresolves your proxy's exit IP and spoofs WebRTC ICE candidates. Auto-injected when usinggeoip=True(no extra network call) humanize=True— one flag makes all mouse, keyboard, and scroll interactions behave like a real user. Bézier curves, per-character typing, realistic scroll patterns- Stealthy with zero flags — binary auto-generates a random fingerprint seed at startup. No configuration required
- Timezone & locale from proxy IP —
launch(proxy="...", geoip=True)auto-detects timezone and locale - Persistent profiles —
launch_persistent_context()keeps cookies and localStorage across sessions, bypasses incognito detection
See the full CHANGELOG.md for details.
Why CloakBrowser?
- Config-level patches break —
playwright-stealth,undetected-chromedriver, andpuppeteer-extrainject JavaScript or tweak flags. Every Chrome update breaks them. Antibot systems detect the patches themselves. - CloakBrowser patches Chromium source code — fingerprints are modified at the C++ level, compiled into the binary. Detection sites see a real browser because it is a real browser.
- Source-level stealth — C++ patches handle fingerprints (GPU, screen, UA, hardware reporting) at the binary level. No JavaScript injection, no config-level hacks. Most stealth tools only patch at the surface.
- Same behavior everywhere — works identically local, in Docker, and on VPS. No environment-specific patches or config needed.
- Works with AI agents and automation frameworks — drop-in stealth for browser-use, Crawl4AI, Scrapling, Stagehand, LangChain, Selenium, and more. See integrations.
CloakBrowser doesn't solve CAPTCHAs — it prevents them from appearing. No CAPTCHA-solving services, no proxy rotation built in — bring your own proxies, use the Playwright API you already know.
CloakBrowser Pro
Anti-bot systems change every week and an older binary quietly degrades. The latest build is the one that keeps passing. Try it free, then upgrade when you're running for real.
- Free, latest build (Chromium 151) — the newest binary, the exact one that stays green against live detection. Free with a GitHub sign-in, one concurrent session. Grab your key or run
cloakbrowser login, then throw it at your hardest target. - Pro — when it's part of production scraping, QA, monitoring, or automation: scale to 5, 20, 200, 2,000, or more concurrent sessions, always first on the newest patches, with hands-on support. Linux, Windows, macOS. See plans and pricing →
- v146 — the older build stays free on GitHub Releases. A quick first look, but it ages fast as detection evolves.
cloakbrowser login # GitHub sign-in for a free key, or paste a paid key
# ...or set it directly (env var, license_key= param, or ~/.cloakbrowser/license.key):
export CLOAKBROWSER_LICENSE_KEY=cb_xxxxxxxxTry the latest free → cloakbrowser.dev/free · Scale up on Pro → cloakbrowser.dev
CloakBrowser Manager
The anti-detect browser, with a GUI. A self-hosted alternative to Multilogin, GoLogin, and AdsPower, running on this same engine, on your own machine, not someone else's cloud.
Every profile is a genuinely separate computer: its own fingerprint, GPU, screen, timezone, proxy, cookies, and history. Nothing bleeds between them. Close a profile, reopen it next month, and it's the same person, warmed up and ready.
- Unlimited profiles — the big brands charge per profile and push you up a tier when you hit the ceiling. Here profiles are unlimited and you pay only for how many run at the same time. A drawer full of dormant accounts costs nothing.
- Native desktop app — profiles open as normal windows on Windows and macOS. On a Linux server they run in Docker and stream to your browser.
- The real engine — most profile managers inject JavaScript into a stock browser. Every profile here runs the patched Chromium binary this repo ships.
- Automate what you can see — every running profile exposes a CDP endpoint. Drive it with Playwright or Puppeteer while you watch the exact same window live.
- One key, both products — your CloakBrowser key works in the Manager too. Free with a GitHub sign-in for one profile at a time.
It's also the fastest way to try the browser without writing any code: download, create a profile, click Launch.
Get the Manager → · Manager repo — the GUI is open source (MIT)
Test Results
All tests verified against live detection services. Results below are for the latest Pro/current build unless noted. Last tested: Aug 2026 (Chromium 151).
| Detection Service | Stock Playwright | CloakBrowser | Notes |
|---|---|---|---|
| reCAPTCHA v3 | 0.1 (bot) | 0.9 (human) | Pro/current build; server-side verified |
| Cloudflare Turnstile (non-interactive) | FAIL | PASS | Auto-resolve |
| Cloudflare Turnstile (managed) | FAIL | PASS | Single click |
| ShieldSquare | BLOCKED | PASS | Production site |
| FingerprintJS bot detection | DETECTED | PASS | Pro/current build; demo.fingerprint.com |
| BrowserScan bot detection | DETECTED | NORMAL (4/4) | browserscan.net |
| bot.incolumitas.com | 13 fails | 1 fail | WEBDRIVER spec only |
| deviceandbrowserinfo.com | 6 true flags | 0 true flags | isBot: false |
navigator.webdriver |
true |
false |
Source-level patch |
navigator.plugins.length |
0 | 5 | Real plugin list |
window.chrome |
undefined |
object |
Present like real Chrome |
| UA string | HeadlessChrome |
Chrome/151.0.0.0 |
No headless leak |
| CDP detection | Detected | Not detected | isAutomatedWithCDP: false |
| TLS fingerprint | Mismatch | Identical to Chrome | ja3n/ja4/akamai match |
| Tested against 30+ detection sites |
Proof

Pro/latest build: reCAPTCHA v3 score 0.9 — server-side verified (human-level)

Cloudflare Turnstile non-interactive challenge — auto-resolved

BrowserScan bot detection — NORMAL (4/4 checks passed)

Pro/latest build: FingerprintJS web-scraping demo — data served, not blocked

deviceandbrowserinfo.com behavioral bot detection — "You are human!" with humanize=True (24/24 signals passed)
Comparison
| Feature | Playwright | playwright-stealth | undetected-chromedriver | CloakBrowser |
|---|---|---|---|---|
| reCAPTCHA v3 score (Pro/current) | 0.1 | 0.3-0.5 | 0.3-0.7 | 0.9 |
| Cloudflare Turnstile | Fail | Sometimes | Sometimes | Pass |
| Headless / Docker detection | Flagged | Flagged | Flagged | Passes |
| Patch level | None | JS injection | Config patches | C++ (Chromium) |
| Survives Chrome updates | N/A | Breaks often | Breaks often | Yes |
| Maintained | Yes | Stale | Stale | Active |
| Browser engine | Chromium | Chromium | Chrome | Chromium |
| Playwright API | Native | Native | No (Selenium) | Native |
How It Works
CloakBrowser is a thin wrapper (Python + JavaScript) around a custom-built Chromium binary:
- You install →
pip install cloakbrowserornpm install cloakbrowser - First launch → binary auto-downloads for your platform (Chromium 146)
- Every launch → Playwright or Puppeteer starts with our binary + stealth args
- You write code → standard Playwright/Puppeteer API, nothing new to learn
The binary includes 87 source-level patches covering canvas, WebGL, audio, fonts, GPU, screen properties, WebRTC, network timing, hardware reporting, automation signal removal, and CDP input behavior mimicking.
These are compiled into the Chromium binary — not injected via JavaScript, not set via flags.
Binary downloads are verified against a pinned Ed25519 signature on the published checksums before extraction, so the download is confirmed authentic (genuinely ours) and not just intact. A compromised mirror cannot serve a tampered or downgraded binary.
API
launch()
from cloakbrowser import launch
# Basic — headless, default stealth config
browser = launch()
# Headed mode (see the browser window)
browser = launch(headless=False)
# Latest binary — pass a key (free via `cloakbrowser login`, or paid) or set CLOAKBROWSER_LICENSE_KEY
browser = launch(license_key="cb_xxxxxxxx")
# With proxy (HTTP or SOCKS5)
browser = launch(proxy="http://user:pass@proxy:8080")
browser = launch(proxy="socks5://user:pass@proxy:1080")
# With proxy dict (bypass, separate auth fields)
browser = launch(proxy={"server": "http://proxy:8080", "bypass": ".google.com", "username": "user", "password": "pass"})
# With extra Chrome args
browser = launch(args=["--disable-gpu"])
# With timezone and locale (sets binary flags — no detectable CDP emulation)
browser = launch(timezone="America/New_York", locale="en-US")
# Auto-detect timezone/locale from proxy IP (requires: pip install cloakbrowser[geoip])
# Also auto-injects --fingerprint-webrtc-ip to prevent WebRTC IP leaks (no extra cost)
# Note: makes HTTP calls through your proxy to resolve exit IP (ipify.org, checkip.amazonaws.com)
browser = launch(proxy="http://proxy:8080", geoip=True)
# Explicit timezone/locale always win over auto-detection
browser = launch(proxy="http://proxy:8080", geoip=True, timezone="Europe/London")
# WebRTC IP spoofing only (no geoip dep needed — resolves exit IP via HTTP call through proxy)
browser = launch(proxy="http://proxy:8080", args=["--fingerprint-webrtc-ip=auto"])
# Explicit WebRTC IP (no network call)
browser = launch(proxy="http://proxy:8080", args=["--fingerprint-webrtc-ip=1.2.3.4"])
# Human-like mouse, keyboard, and scroll behavior
browser = launch(humanize=True)
# With slower, more deliberate movements
browser = launch(humanize=True, human_preset="careful")
# Without default stealth args (bring your own fingerprint flags)
browser = launch(stealth_args=False, args=["--fingerprint=12345"])Returns a standard Playwright Browser object. All Playwright methods work: new_page(), new_context(), close(), etc.
launch_async()
import asyncio
from cloakbrowser import launch_async
async def main():
browser = await launch_async()
page = await browser.new_page()
await page.goto("https://example.com")
print(await page.title())
await browser.close()
asyncio.run(main())launch_context()
Convenience function that creates browser + context in one call with user agent, viewport, locale, and timezone:
from cloakbrowser import launch_context
context = launch_context(
user_agent="Custom UA",
viewport={"width": 1920, "height": 1080},
locale="en-US",
timezone="America/New_York",
)
page = context.new_page()
page.goto("https://protected-site.com")
context.close()Extra kwargs are forwarded to Playwright's browser.new_context() — use this for storage_state, permissions, extra_http_headers, etc. without needing a persistent profile folder:
from cloakbrowser import launch_context
# Restore a saved session (cookies, localStorage) from a JSON file
context = launch_context(storage_state="state.json")
page = context.new_page()
page.goto("https://example.com")
# Save state back for next run
context.storage_state(path="state.json")
context.close()launch_context_async()
Async counterpart to launch_context(). Same signature and kwargs forwarding:
import asyncio
from cloakbrowser import launch_context_async
async def main():
ctx = await launch_context_async(storage_state="state.json")
page = await ctx.new_page()
await page.goto("https://example.com")
await ctx.storage_state(path="state.json")
await ctx.close()
asyncio.run(main())launch_persistent_context()
Same as launch_context(), but with a persistent user profile. Cookies, localStorage, and cache persist across sessions.
Use this when you need to:
- Stay logged in across runs (cookies/sessions survive restarts)
- Bypass incognito detection (some sites flag empty, ephemeral profiles)
- Load Chrome extensions (extensions only work from a real user data dir)
- Build natural browsing history (cached fonts, service workers, IndexedDB accumulate over time, making the profile look more realistic)
- Play DRM-protected video (Widevine) — with a sideloaded CDM, the wrapper enables Widevine on the first launch (see Widevine / DRM)
from cloakbrowser import launch_persistent_context
# First run — creates the profile
ctx = launch_persistent_context("./my-profile", headless=False)
page = ctx.new_page()
page.goto("https://protected-site.com")
ctx.close() # profile saved
# Next run — cookies, localStorage restored automatically
ctx = launch_persistent_context("./my-profile", headless=False)
# Load Chrome extensions
ctx = launch_persistent_context(
"./my-profile",
headless=False,
extension_paths=["./my-extension"],
)Supports all the same options as launch_context(): proxy, user_agent, viewport, locale, timezone, color_scheme, geoip, extension_paths.
Async version: launch_persistent_context_async().
Storage quota and incognito detection: the binary normalizes storage quota by default (this also hides the real disk size). Detectors that infer private/incognito mode from quota — e.g. BrowserScan's incognito check (−10%) — read the default as incognito. Raise it to present as a regular profile:
ctx = launch_persistent_context("./my-profile", args=["--fingerprint-storage-quota=5000"])Widevine / DRM
The binary is built with Widevine support, but the Widevine CDM is a proprietary Google component we can't redistribute. Get it one of two ways (full background in #96):
Fetch it — no Chrome install needed; pulls the CDM from Google's component server (Linux x86-64 only; SHA-256 + CRX3-signature verified). It lands at ~/.cloakbrowser/WidevineCdm, which the wrapper auto-detects — no env var needed:
python3 bin/fetch-widevine.pyOr copy it from an existing Chrome install, next to the binary:
cp -r /opt/google/chrome/WidevineCdm ~/.cloakbrowser/chromium-<version>/WidevineCdm(In Docker, just pass -e CLOAKBROWSER_FETCH_WIDEVINE=1 — the entrypoint runs the fetch automatically; see the Docker note below.)
With the CDM in place, launch_persistent_context() enables Widevine on the first launch — the wrapper auto-writes the CDM hint file into the profile, so you don't need the manual two-launch workaround. This lets you play DRM-protected video (e.g. Netflix, Spotify Web).
from cloakbrowser import launch_persistent_context
# WidevineCdm sideloaded next to the binary -> Widevine works on first launch
ctx = launch_persistent_context("./my-profile", headless=False)- Linux only. Chromium's hint-file mechanism is Linux/ChromeOS-specific. On Windows the CDM can't initialise (DRM host verification) and macOS uses a different layout, so seeding is a no-op there.
- Auto by presence. No flag needed — a sideloaded CDM is the opt-in. Point at a CDM in a non-default location with
CLOAKBROWSER_WIDEVINE_CDM=/path/to/WidevineCdm, or disable seeding entirely withCLOAKBROWSER_WIDEVINE=0. - Docker — auto-fetch (opt-in). No Chrome to copy from inside the image, so the official image can fetch the CDM for you. Run with
-e CLOAKBROWSER_FETCH_WIDEVINE=1and it pulls the CDM from Google's component server (the same source Chrome uses) on first launch, caches it at~/.cloakbrowser/WidevineCdmin the mounted volume, where the wrapper auto-detects it — for free or Pro binaries, and fordocker exec'd scripts alike. Off by default — no network call unless you opt in — and best-effort, so a failed fetch never blocks launch. The download is signature- and checksum-verified before install. Bare-metal Linux users can run the same fetcher directly:python3 bin/fetch-widevine.py(pip-only installs can grab that one self-contained file from the repo).
CLI
Pre-download the binary, diagnose your setup, or manage the cache from the command line:
python -m cloakbrowser login # Get a free key via GitHub, or save a paid key
python -m cloakbrowser logout # Remove the saved key (revert to the free binary)
python -m cloakbrowser install # Download binary with progress output
python -m cloakbrowser info # Diagnostics: binary that will launch, license tier, env checks
python -m cloakbrowser update # Check for and download newer binary
python -m cloakbrowser clear-cache # Remove cached binarieslogin with no argument prompts you to paste a license key or press Enter to get a free key via a GitHub sign-in; login <key> saves a key directly. Both validate the key, then store it at ~/.cloakbrowser/license.key so every launch picks it up.
info reports the binary that will actually launch given your license, runs a quick launch test (and flags missing system libraries on Linux), shows your license tier, and checks fonts, GeoIP, and optional dependencies. Add --quick to skip the launch test or --json for machine-readable output. Add --proxy <url> to resolve the exit IP, timezone, and locale a launch would apply through that proxy (the same geoip=True resolution; downloads the GeoIP DB if not cached) — useful for confirming a proxy hands you a timezone/locale that matches its exit IP.
CLOAKBROWSER_RELEASE_CHANNEL=preview also applies to install, info, and update. info shows the exact version that will launch and whether Preview resolved to Stable for the current platform.
The same commands are available via npx cloakbrowser <command> (JS) and the cloakbrowser CLI (.NET).
Utility Functions
from cloakbrowser import binary_info, clear_cache, ensure_binary
# Check binary installation status
print(binary_info())
# {'version': '146.0.7680.177.5', 'platform': 'linux-x64', 'installed': True, ...}
# Force re-download
clear_cache()
# Pre-download binary (e.g., during Docker build)
ensure_binary()JavaScript / Node.js API
CloakBrowser ships a TypeScript package with full type definitions. Choose Playwright or Puppeteer — same stealth binary underneath.
Playwright (default)
import { launch, launchContext, launchPersistentContext } from 'cloakbrowser';
// Basic
const browser = await launch();
// Pro — use the latest binary (or set CLOAKBROWSER_LICENSE_KEY env var)
const browser = await launch({ licenseKey: 'cb_xxxxxxxx' });
// With options
const browser = await launch({
headless: false,
proxy: 'http://user:pass@proxy:8080',
args: ['--fingerprint=12345'],
timezone: 'America/New_York',
locale: 'en-US',
humanize: true,
});
// Convenience: browser + context in one call
const context = await launchContext({
userAgent: 'Custom UA',
viewport: { width: 1920, height: 1080 },
locale: 'en-US',
timezone: 'America/New_York',
});
const page = await context.newPage();
// Persistent profile — cookies/localStorage survive restarts, avoids incognito detection
const ctx = await launchPersistentContext({
userDataDir: './chrome-profile',
headless: false,
proxy: 'http://user:pass@proxy:8080',
});Note: Each example above is standalone — not meant to run as one block.
All Python options work in JS: stealthArgs: false to disable defaults, geoip: true to auto-detect timezone/locale from proxy IP.
Puppeteer
Note: The Playwright wrapper is recommended for sites with reCAPTCHA Enterprise. Puppeteer's CDP protocol leaks automation signals that reCAPTCHA Enterprise can detect, causing intermittent 403 errors. This is a known Puppeteer limitation, not specific to CloakBrowser. Use Playwright for best results.
import { launch } from 'cloakbrowser/puppeteer';
const browser = await launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();Utility Functions (JS)
import { ensureBinary, clearCache, binaryInfo } from 'cloakbrowser';
// Pre-download binary (e.g., during Docker build)
await ensureBinary();
// Check installation status
console.log(binaryInfo());
// Force re-download
clearCache();Human Behavior
Pass humanize=True to make all mouse, keyboard, and scroll interactions indistinguishable from real users. All Playwright calls (page.click(), page.fill(), page.type(), page.mouse.*, page.keyboard.*, Locator API) and Puppeteer calls (page.click(), page.type(), page.mouse.*, page.keyboard.*, ElementHandle API) are automatically replaced with human-like equivalents. No code changes needed.
browser = launch(humanize=True)
page = browser.new_page()
page.goto("https://example.com")
page.locator("#email").fill("[email protected]") # per-character timing, thinking pauses
page.locator("button[type=submit]").click() # Bézier curve, realistic aim point// Playwright
import { launch } from 'cloakbrowser';
const browser = await launch({ humanize: true });// Puppeteer
import { launch } from 'cloakbrowser/puppeteer';
const browser = await launch({ humanize: true });What changes:
| Interaction | Default | With humanize=True |
|---|---|---|
| Mouse movement | Instant teleport | Bézier curve with easing and slight overshoot |
| Clicks | Instant | Realistic aim point + hold duration |
| Keyboard | Instant fill | Per-character timing, thinking pauses, occasional typos with self-correction |
| Scroll | Jump | Accelerate → cruise → decelerate micro-steps |
fill() |
Instant value set | Clears existing content, types character by character |
Presets — default (normal speed) or careful (slower, more deliberate, idle micro-movements between actions):
browser = launch(humanize=True, human_preset="careful")const browser = await launch({ humanize: true, humanPreset: 'careful' });Custom config — override any parameter:
browser = launch(humanize=True, human_config={
"mistype_chance": 0.05, # 5% typo rate with self-correction
"typing_delay": 100, # slower typing (ms per character)
"idle_between_actions": True, # micro-movements between clicks
"idle_between_duration": [0.3, 0.8], # idle duration range (seconds)
})const browser = await launch({
humanize: true,
humanConfig: {
mistype_chance: 0.05,
typing_delay: 100,
idle_between_actions: true,
idle_between_duration: [0.3, 0.8],
}
});Access the original un-patched Playwright page at page._original if you need raw speed for a specific call.
Note (Playwright): Always use
page.click(selector),page.type(selector, text),page.hover(selector), orpage.locator(selector).*— these go through the full humanize pipeline. Avoidpage.query_selector()—ElementHandleobjects bypass all patches, so mouse movement teleports, keyboard events fire without timing, and scroll has no human curve.Note (Puppeteer): Both selector-based methods (
page.click(),page.type()) and ElementHandle methods (el.click(),el.type()) are fully humanized.page.$(),page.$$(), andpage.waitForSelector()return patched handles automatically.
Contributed by @evelaa123 — full Playwright and Puppeteer API coverage.
Configuration
| Env Variable | Default | Description |
|---|---|---|
CLOAKBROWSER_BINARY_PATH |
— | Skip download, use a local Chromium binary |
CLOAKBROWSER_CACHE_DIR |
~/.cloakbrowser |
Binary cache directory |
CLOAKBROWSER_DOWNLOAD_URL |
cloakbrowser.dev |
Custom download URL for binary |
CLOAKBROWSER_AUTO_UPDATE |
true |
Set to false to disable background update checks |
CLOAKBROWSER_SKIP_CHECKSUM |
false |
Only applies to a custom CLOAKBROWSER_DOWNLOAD_URL: set to true to skip its checksum check. Signature verification on the official download path is mandatory and cannot be skipped. |
CLOAKBROWSER_GEOIP_TIMEOUT_SECONDS |
5 |
Max seconds for GeoIP resolution before continuing without it |
CLOAKBROWSER_WIDEVINE_CDM |
— | Path to a sideloaded WidevineCdm directory (overrides auto-detection next to the binary). See Widevine / DRM |
CLOAKBROWSER_WIDEVINE |
1 |
Set to 0 to disable automatic Widevine hint-file seeding for persistent contexts |
CLOAKBROWSER_FETCH_WIDEVINE |
0 |
Docker only: set to 1 to auto-fetch the Widevine CDM on container start (Linux x86-64 only). See Widevine / DRM |
CLOAKBROWSER_VERSION |
— | Pin to an exact Chromium version for rollback (e.g. 148.0.7778.215.2). Works with Free and Pro binaries |
CLOAKBROWSER_RELEASE_CHANNEL |
stable |
Set to preview to opt into the Preview binary channel |
Fingerprint Management
The binary is stealthy by default — no flags needed. It auto-generates a random fingerprint seed at startup and spoofs all detectable values (GPU, hardware specs, screen dimensions, canvas, WebGL, audio, fonts). Every launch produces a fresh, coherent identity.
How fingerprinting works:
| Scenario | What happens |
|---|---|
| No flags | Random seed auto-generated at startup. GPU, screen, hardware specs, and all noise patches are spoofed automatically. Fresh identity each launch. |
--fingerprint=seed |
Deterministic identity from the seed. Same seed = same fingerprint across launches. Use this for session persistence (returning visitor). |
--fingerprint=seed + explicit flags |
Explicit flags override individual auto-generated values. The seed fills in everything else. |
The binary detects its platform at compile time — a macOS binary reports as macOS with Apple GPU, a Linux binary reports as Linux with NVIDIA GPU. The wrapper overrides this on Linux by passing --fingerprint-platform=windows, so sessions appear as Windows desktops (more common fingerprint, harder to cluster). Use --fingerprint-platform for cross-platform spoofing when running the binary directly.
macOS persona (a second identity). Alongside the Windows persona, pass --fingerprint-platform=macos to present as a Mac — lined up with real Mac hardware across graphics, media, fonts, text, screen, and device features. Two things to set up: install the Mac fonts (the same way the Windows persona needs Windows fonts — see Font Setup on Linux; a persona without its fonts is a common cause of blocks), and for the most realistic result give it a large display — on a 4K screen it reports at the DPR most real Macs use. Having both the Windows and macOS personas gives you two strong identities to rotate between.
Tip: Use a fixed seed when revisiting the same site. A random seed makes every session look like a different device — which can be suspicious when hitting the same site repeatedly from the same IP. For reCAPTCHA v3 Enterprise and similar scoring systems, a fixed seed produces a consistent fingerprint across sessions, making you look like a returning visitor:
browser = launch(args=["--fingerprint=12345"])const browser = await launch({ args: ['--fingerprint=12345'] });
Default Fingerprint
Every launch() call sets these automatically. The wrapper applies platform-aware defaults — on Linux it spoofs as Windows for a more common fingerprint, on macOS it runs as a native Mac browser:
| Flag | Linux/Windows Default | macOS Default | Controls |
|---|---|---|---|
--fingerprint |
Random (10000–99999) | Random (10000–99999) | Master seed for canvas, WebGL, audio, fonts, client rects |
--fingerprint-platform |
windows |
macos |
navigator.platform, User-Agent OS, GPU pool selection |
The binary auto-generates everything else from the seed: GPU, hardware concurrency, device memory, and screen dimensions. Each seed produces a unique, consistent fingerprint. Override with explicit flags if needed.
Using the binary directly? It works out of the box with zero flags -- the binary auto-spoofs everything. Pass
--fingerprint=seedfor a persistent identity, or use explicit flags like--fingerprint-gpu-rendererto override any auto-generated value.
Additional Flags
Supported by the binary but not set by default — pass via args to customize:
| Flag | Controls | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
--fingerprint-gpu-vendor |
WebGL UNMASKED_VENDOR_WEBGL (auto-generated from seed + platform) |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--fingerprint-gpu-renderer |
WebGL UNMASKED_RENDERER_WEBGL (auto-generated from seed + platform) |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--fin
|
