| 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.
- 32 source-level C++ patches — canvas, WebGL, audio, fonts, GPU, screen, automation signals, CDP input behavior
humanize=True— human-like mouse curves, keyboard timing, and scroll patterns. One flag, behavioral detection passes- 0.9 reCAPTCHA v3 score — human-level, server-verified
- Passes Cloudflare Turnstile, FingerprintJS, BrowserScan — tested against 30+ detection sites
- Auto-updating binary — background update checks, always on the latest stealth build
pip install cloakbrowserornpm install cloakbrowser— binary auto-downloads, zero config- Free and open source — no subscriptions, no usage limits
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://protected-site.com") # no more blocks
browser.close()JavaScript (Playwright):
import { launch } from 'cloakbrowser';
const browser = await launch();
const page = await browser.newPage();
await page.goto('https://protected-site.com');
await browser.close();Also works with Puppeteer: import { launch } from 'cloakbrowser/puppeteer' (details)
Python:
pip install cloakbrowserJavaScript / Node.js:
# With Playwright
npm install cloakbrowser playwright-core
# With Puppeteer
npm install cloakbrowser puppeteer-coreOn 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.
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. Two presets:defaultandcareful- CDP input behavior mimicking — input events sent via CDP now produce the same signals as real user interactions. 5 new source-level patches covering pointer, keyboard, and mouse behavior
- Native locale spoofing — new C++ patch replaces detectable CDP-level locale emulation
- WebGPU fingerprint hardening — adapter features, limits, and device ID spoofed for cross-API consistency
- 32 fingerprint patches (Linux x64) — all 4 platforms on Chromium 145
- 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.
- 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, agent-browser, Claude computer use, and OpenAI Operator. Also tested with Playwright, Puppeteer, and Selenium — point any Chromium-based framework at the binary path.
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.
All tests verified against live detection services. Last tested: Mar 2026 (Chromium 145).
| Detection Service | Stock Playwright | CloakBrowser | Notes |
|---|---|---|---|
| reCAPTCHA v3 | 0.1 (bot) | 0.9 (human) | 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 | 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 |
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 |

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)

FingerprintJS web-scraping demo — data served, not blocked

deviceandbrowserinfo.com behavioral bot detection — "You are human!" with humanize=True (24/24 signals passed)
| Feature | Playwright | playwright-stealth | undetected-chromedriver | Camoufox | CloakBrowser |
|---|---|---|---|---|---|
| reCAPTCHA v3 score | 0.1 | 0.3-0.5 | 0.3-0.7 | 0.7-0.9 | 0.9 |
| Cloudflare Turnstile | Fail | Sometimes | Sometimes | Pass | Pass |
| Patch level | None | JS injection | Config patches | C++ (Firefox) | C++ (Chromium) |
| Survives Chrome updates | N/A | Breaks often | Breaks often | Yes | Yes |
| Maintained | Yes | Stale | Stale | Unstable | Active |
| Browser engine | Chromium | Chromium | Chrome | Firefox | Chromium |
| Playwright API | Native | Native | No (Selenium) | No | Native |
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 145)
- 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 32 source-level patches covering canvas, WebGL, audio, fonts, GPU, screen properties, 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 with SHA-256 checksums to ensure integrity.
from cloakbrowser import launch
# Basic — headless, default stealth config
browser = launch()
# Headed mode (see the browser window)
browser = launch(headless=False)
# With proxy
browser = launch(proxy="http://user:pass@proxy:8080")
# 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])
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")
# 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.
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())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()Same as launch_context(), but with a persistent user profile. Cookies, localStorage, and cache persist across sessions. Also avoids incognito detection by services like BrowserScan.
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)
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)Supports all the same options as launch_context(): proxy, user_agent, viewport, locale, timezone, color_scheme, geoip.
Async version: launch_persistent_context_async().
Pre-download the binary or check installation status from the command line:
python -m cloakbrowser install # Download binary with progress output
python -m cloakbrowser info # Show version, path, platform
python -m cloakbrowser update # Check for and download newer binary
python -m cloakbrowser clear-cache # Remove cached binariesfrom cloakbrowser import binary_info, clear_cache, ensure_binary
# Check binary installation status
print(binary_info())
# {'version': '145.0.7632.159.2', 'platform': 'linux-x64', 'installed': True, ...}
# Force re-download
clear_cache()
# Pre-download binary (e.g., during Docker build)
ensure_binary()CloakBrowser ships a TypeScript package with full type definitions. Choose Playwright or Puppeteer — same stealth binary underneath.
import { launch, launchContext, launchPersistentContext } from 'cloakbrowser';
// Basic
const browser = await launch();
// 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.
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();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();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.*, and the full Locator 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("user@example.com") # per-character timing, thinking pauses
page.locator("button[type=submit]").click() # Bézier curve, realistic aim pointconst 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.
Contributed by @evelaa123 — full Playwright API coverage.
| 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 |
Set to true to skip SHA-256 verification after download |
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.
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'] });
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 |
--fingerprint-gpu-vendor |
NVIDIA Corporation |
Google Inc. (Apple) |
WebGL UNMASKED_VENDOR_WEBGL |
--fingerprint-gpu-renderer |
NVIDIA GeForce RTX 3070 |
ANGLE (Apple, ANGLE Metal Renderer: Apple M3, Unspecified Version) |
WebGL UNMASKED_RENDERER_WEBGL |
The binary auto-generates hardware concurrency (8), device memory (8), and screen dimensions (1920x1080 on Windows/Linux, 1440x900 on macOS) from the seed. 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.
Production tip: For better stealth at scale, pass your own GPU, screen, and hardware values instead of relying on defaults. Custom parameters make your sessions harder to cluster by anti-bot systems that look for uniform fingerprint profiles.
Supported by the binary but not set by default — pass via args to customize:
| Flag | Controls |
|---|---|
--fingerprint-hardware-concurrency |
navigator.hardwareConcurrency (auto-generated: 8) |
--fingerprint-device-memory |
navigator.deviceMemory in GB (auto-generated: 8) |
--fingerprint-screen-width |
Screen width (auto-generated: 1920 Win/Linux, 1440 macOS) |
--fingerprint-screen-height |
Screen height (auto-generated: 1080 Win/Linux, 900 macOS) |
--fingerprint-brand |
Browser brand: Chrome, Edge, Opera, Vivaldi |
--fingerprint-brand-version |
Brand version (UA + Client Hints) |
--fingerprint-platform-version |
Client Hints platform version |
--fingerprint-location |
Geolocation coordinates |
--fingerprint-timezone |
Timezone (e.g. America/New_York) |
--fingerprint-locale |
Locale (e.g. en-US) |
--fingerprint-taskbar-height |
Override taskbar height (binary defaults: Win=48, Mac=95, Linux=0) |
--fingerprint-fonts-dir |
Path to cross-platform font directory |
--enable-blink-features=FakeShadowRoot |
Access closed shadow DOM elements |
Note: All stealth tests were verified with the default fingerprint config above. Changing these flags may affect detection results — test your configuration before using in production.
# Pin a seed for a persistent identity
browser = launch(args=["--fingerprint=42069"])
# Full control — disable defaults, set everything yourself
browser = launch(stealth_args=False, args=[
"--fingerprint=42069",
"--fingerprint-platform=windows",
"--fingerprint-gpu-vendor=NVIDIA Corporation",
"--fingerprint-gpu-renderer=NVIDIA GeForce RTX 3070",
])
# Override GPU to look like a different machine
browser = launch(args=[
"--fingerprint-gpu-vendor=Intel Inc.",
"--fingerprint-gpu-renderer=Intel Iris OpenGL Engine",
])Python — see examples/:
basic.py— Launch and load a pagepersistent_context.py— Persistent profile with cookie/localStorage persistencerecaptcha_score.py— Check your reCAPTCHA v3 scorestealth_test.py— Run against 6 detection sitesfingerprint_scan_test.py— Test against fingerprint-scan.com and CreepJS
JavaScript — see js/examples/:
basic-playwright.ts— Playwright launch and loadbasic-puppeteer.ts— Puppeteer launch and loadstealth-test.ts— Run against 6 detection sites
| Platform | Chromium | Patches | Status |
|---|---|---|---|
| Linux x86_64 | 145 | 31 | ✅ Latest |
| macOS arm64 (Apple Silicon) | 145 | 26 | ✅ Latest |
| macOS x86_64 (Intel) | 145 | 26 | ✅ Latest |
| Windows x86_64 | 145 | 26 | ✅ Latest |
The wrapper auto-downloads the correct binary for your platform.
macOS first launch: The binary is ad-hoc signed. On first run, macOS Gatekeeper will block it. Right-click the app → Open → click Open in the dialog. This is only needed once.
Pre-built image on Docker Hub — no install, no setup.
docker run --rm cloakhq/cloakbrowser cloaktest# Inline script
docker run --rm cloakhq/cloakbrowser python -c "
from cloakbrowser import launch
browser = launch()
page = browser.new_page()
page.goto('https://example.com')
print(page.title())
browser.close()
"
# Mount your own script
docker run --rm -v ./my_script.py:/app/my_script.py cloakhq/cloakbrowser python my_script.py
# With a proxy
docker run --rm cloakhq/cloakbrowser python -c "
from cloakbrowser import launch
browser = launch(proxy='http://user:pass@proxy:8080')
page = browser.new_page()
page.goto('https://example.com')
print(page.title())
browser.close()
"Start a persistent stealth browser and connect to it remotely via Chrome DevTools Protocol:
docker run -d --name cloak -p 127.0.0.1:9222:9222 cloakhq/cloakbrowser cloakserveThen connect from your host machine:
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
browser = pw.chromium.connect_over_cdp("http://localhost:9222")
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close()Pass extra flags to the browser:
# With proxy
docker run -d --name cloak -p 127.0.0.1:9222:9222 cloakhq/cloakbrowser \
cloakserve --proxy-server=http://proxy:8080
# Headed mode (renders to Xvfb inside container)
docker run -d --name cloak -p 127.0.0.1:9222:9222 cloakhq/cloakbrowser \
cloakserve --headless=falseStop the server:
docker stop cloak && docker rm cloakSecurity: CDP gives full control over the browser (execute JS, read pages, access files). The examples bind to
127.0.0.1so only your machine can connect. Never expose port 9222 to the public internet without additional authentication.
services:
cloakbrowser:
image: cloakhq/cloakbrowser
command: cloakserve
restart: unless-stopped
ports:
- "127.0.0.1:9222:9222"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9222/json/version"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10sRun multiple instances with different fingerprint seeds on different ports — each gets unique canvas noise, client rects, and other browser signals. Pass --fingerprint=<seed> in the command (e.g., cloakserve --fingerprint=12345).
Persistent profiles — mount a volume to keep cookies and sessions across container restarts:
docker run --rm -v ./my-profile:/profile cloakhq/cloakbrowser python -c "
from cloakbrowser import launch_persistent_context
ctx = launch_persistent_context('/profile')
page = ctx.new_page()
page.goto('https://example.com')
ctx.close()
"Run again with the same volume — cookies, localStorage, and cache are restored automatically.
Resource usage: ~190MB RAM idle, ~280MB with 3 tabs. ~30MB per additional tab.
FROM cloakhq/cloakbrowser
COPY your_script.py /app/
CMD ["python", "your_script.py"]Building your own image from pip — use python -m cloakbrowser install to download the binary during build with visible progress:
FROM python:3.12-slim
RUN pip install cloakbrowser && python -m cloakbrowser install
COPY your_script.py /app/
CMD ["python", "/app/your_script.py"]Building from source — a Dockerfile is also included if you prefer to build your own image:
docker build -t cloakbrowser .CloakBrowser works identically local, in Docker, and on VPS. No environment-specific config needed.
Note: If you run CloakBrowser inside a web server with uvloop (e.g., uvicorn[standard]), use --loop asyncio to avoid subprocess pipe hangs.
Some sites detect headless mode even with our C++ patches. Run in headed mode with a virtual display:
# Install Xvfb (virtual framebuffer)
sudo apt install xvfb
# Start virtual display
Xvfb :99 -screen 0 1920x1080x24 &
export DISPLAY=:99from cloakbrowser import launch
# Headed mode + residential proxy for maximum stealth
browser = launch(headless=False, proxy="http://your-residential-proxy:port")
page = browser.new_page()
page.goto("https://heavily-protected-site.com") # passes DataDome, etc.
browser.close()This runs a real headed browser rendered on a virtual display — no physical monitor needed. Combined with a residential proxy, this passes even the most aggressive detection services. Datacenter IPs are often flagged by IP reputation regardless of browser fingerprint — a residential proxy makes the difference.
Some sites challenge first-time visitors with no cookies over HTTP/2. This affects all Chromium browsers, not just CloakBrowser. Use a persistent profile to warm up cookies once, then reuse across sessions:
from cloakbrowser import launch_persistent_context
# First run: warm up with --disable-http2
ctx = launch_persistent_context("./profile", args=["--disable-http2"])
page = ctx.new_page()
page.goto("https://example.com") # warms up cookies
ctx.close()
# Future runs — no --disable-http2 needed
ctx = launch_persistent_context("./profile")
page = ctx.new_page()
page.goto("https://example.com") # passes with saved cookiesimport { launchPersistentContext } from 'cloakbrowser';
// First run: warm up with --disable-http2
let ctx = await launchPersistentContext({ userDataDir: './profile', args: ['--disable-http2'] });
let page = await ctx.newPage();
await page.goto('https://example.com');
await ctx.close();
// Future runs — no --disable-http2 needed
ctx = await launchPersistentContext({ userDataDir: './profile' });For stateless/ephemeral use cases, launch(args=["--disable-http2"]) forces HTTP/1.1 which bypasses the check. Only use this flag for sites that require it — most work fine with HTTP/2.
Older versions may use outdated stealth args or download an older binary:
pip install -U cloakbrowser # Python
npm install cloakbrowser@latest # JavaScript
docker pull cloakhq/cloakbrowser:latest # DockerSet a custom download URL or use a local binary:
export CLOAKBROWSER_BINARY_PATH=/path/to/your/chromeInstall a specific wrapper version to downgrade both the wrapper and the binary it downloads:
pip install cloakbrowser==0.3.11 # Python
npm install cloakbrowser@0.3.11 # JavaScript
docker pull cloakhq/cloakbrowser:0.3.11 # DockerEach wrapper version pins its own binary version, so downgrading the wrapper automatically gets you the matching binary on next launch.
The binary is ad-hoc signed. macOS quarantines downloaded files. Run once to clear it:
xattr -cr ~/.cloakbrowser/chromium-*/Chromium.appYou do NOT need playwright install chromium. CloakBrowser downloads its own binary. You only need Playwright's system deps:
playwright install-deps chromiumThe macOS fingerprint profile has known inconsistencies that aggressive bot detection catches. If a site blocks you on macOS but works on Linux, switch to a Windows fingerprint profile by passing stealth_args=False and manually setting --fingerprint-platform=windows with matching GPU flags (see Fingerprint Management for the full flag list).
By default, launch() opens an incognito context. Some sites (like BrowserScan) detect this. Use launch_persistent_context() instead — it runs with a real user profile, so incognito detection passes:
from cloakbrowser import launch_persistent_context
ctx = launch_persistent_context("./my-profile", headless=False)
page = ctx.new_page()import { launchPersistentContext } from 'cloakbrowser';
const ctx = await launchPersistentContext({
userDataDir: './my-profile',
headless: false,
});This also gives you cookie and localStorage persistence across sessions.
Avoid page.wait_for_timeout() — it sends CDP protocol commands that reCAPTCHA detects. Use native sleep instead:
# Bad — sends CDP commands, reCAPTCHA detects this
page.wait_for_timeout(3000)
# Good — invisible to the browser
import time
time.sleep(3)// Bad — sends CDP commands
await page.waitForTimeout(3000);
// Good — invisible to the browser
await new Promise(r => setTimeout(r, 3000));Other tips for maximizing reCAPTCHA scores:
- Try the Patchright backend — suppresses additional CDP automation signals at the Playwright protocol layer. Install with
pip install cloakbrowser[patchright], then uselaunch(backend="patchright")or setCLOAKBROWSER_BACKEND=patchrightglobally. Note: Patchright breaks proxy auth andadd_init_script— only use it if you're still seeing low scores after trying the steps above - Use Playwright, not Puppeteer — Puppeteer sends more CDP protocol traffic that reCAPTCHA detects (details)
- Use residential proxies — datacenter IPs are flagged by IP reputation, not browser fingerprint
- Spend 15+ seconds on the page before triggering reCAPTCHA — short visits score lower
- Space out requests — back-to-back
grecaptcha.execute()calls from the same session get penalized. Wait 30+ seconds between pages with reCAPTCHA - Use a fixed fingerprint seed for consistent device identity across sessions (see Fingerprint Management)
- Use
page.type()instead ofpage.fill()for form filling —fill()sets values directly without keyboard events, which reCAPTCHA's behavioral analysis flags.type()with a delay simulates real keystrokes:page.type("#email", "user@example.com", delay=50)
- Minimize
page.evaluate()calls before the reCAPTCHA check fires — each one sends CDP traffic
Q: Is this legal? A: CloakBrowser is a browser built on open-source Chromium. We do not condone illegal use. Automating systems without authorization, credential stuffing, and account creation abuse are expressly prohibited. See BINARY-LICENSE.md for full terms.
Q: How is this different from Camoufox? A: Camoufox patches Firefox. We patch Chromium. Chromium means native Playwright support, larger ecosystem, and TLS fingerprints that match real Chrome. Camoufox returned in early 2026 but is in unstable beta — CloakBrowser is production-ready.
Q: Will detection sites eventually catch this? A: Possibly. Bot detection is an arms race. Source-level patches are harder to detect than config-level patches, but not impossible. We actively monitor and update when detection evolves.
Q: Can I use my own proxy?
A: Yes. Pass proxy="http://user:pass@host:port" to launch().
| Feature | Status |
|---|---|
| Linux x64 — Chromium 145 (26 patches) | ✅ Released |
| macOS arm64/x64 — Chromium 145 (26 patches) | ✅ Released |
| Windows x64 — Chromium 145 (26 patches) | ✅ Released |
| JavaScript/Puppeteer + Playwright support | ✅ Released |
| Fingerprint rotation per session | ✅ Released |
| Built-in proxy rotation | 📋 Planned |
- 📋 Changelog — CHANGELOG.md
- 🌐 Website — cloakbrowser.dev
- 🐛 Bug reports & feature requests — GitHub Issues
- 📦 PyPI — pypi.org/project/cloakbrowser
- 📦 npm — npmjs.com/package/cloakbrowser
- 📧 Contact — cloakhq@pm.me
All releases are signed for supply chain verification.
# Verify GPG signature (binary release tag)
gpg --keyserver keyserver.ubuntu.com --recv-keys C60C0DDC9D0DE2DD
git verify-tag chromium-v145.0.7632.159.5
# Verify GitHub binary attestation (Sigstore)
gh attestation verify cloakbrowser-linux-x64.tar.gz --repo CloakHQ/cloakbrowser
# Verify Docker image signature (Cosign/Sigstore)
cosign verify \
--certificate-identity-regexp "https://github.com/CloakHQ/CloakBrowser/" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
cloakhq/cloakbrowser:latest- Wrapper code (this repository) — MIT. See LICENSE.
- CloakBrowser binary (compiled Chromium) — free to use, no redistribution. See BINARY-LICENSE.md.
Issues and PRs welcome. If something isn't working, open an issue — we respond fast.
- @evelaa123 — humanize behavior, persistent contexts, Windows fix
- @yahooguntu — persistent contexts
