2026-02-23 19:57:19 +01:00
<p align="center">
2026-02-26 06:17:57 +01:00
<img src="https://i.imgur.com/cqkp6fG.png" width="500" alt="CloakBrowser">
2026-02-23 19:57:19 +01:00
</p>
# CloakBrowser
[](https://www.npmjs.com/package/cloakbrowser)
[](https://github.com/CloakHQ/CloakBrowser/blob/main/LICENSE)
**Stealth Chromium that passes every bot detection test.**
2026-03-05 03:41:36 +01:00
Drop-in Playwright/Puppeteer replacement. Same API, same code — just swap the import. **3 lines of code, 30 seconds to unblock.**
2026-02-23 19:57:19 +01:00
2026-03-13 18:54:05 +01:00
- **33 source-level C++ patches** — canvas, WebGL, audio, fonts, GPU, screen, automation signals
2026-03-05 03:41:36 +01:00
- **0.9 reCAPTCHA v3 score** — human-level, server-verified
- **Passes Cloudflare Turnstile**, FingerprintJS, BrowserScan — tested against 30+ detection sites
- **`npm install cloakbrowser` ** — binary auto-downloads, auto-updates, zero config
- **Free and open source** — no subscriptions, no usage limits
- **Works with any framework** — also tested with Selenium, undetected-chromedriver, browser-use, Crawl4AI, and agent-browser
2026-02-23 19:57:19 +01:00
## Install
```bash
# With Playwright
npm install cloakbrowser playwright-core
# With Puppeteer
npm install cloakbrowser puppeteer-core
```
On first launch, the stealth Chromium binary auto-downloads (~200MB, cached at `~/.cloakbrowser/` ).
## Usage
### Playwright (default)
```javascript
import { launch } from 'cloakbrowser' ;
const browser = await launch ();
const page = await browser . newPage ();
await page . goto ( 'https://protected-site.com' );
console . log ( await page . title ());
await browser . close ();
```
### Puppeteer
2026-02-25 19:20:57 +01:00
> **Note:** Playwright is recommended for sites with reCAPTCHA Enterprise. Puppeteer's CDP protocol leaks automation signals that reCAPTCHA Enterprise can detect. This is a known Puppeteer limitation, not specific to CloakBrowser.
2026-02-23 19:57:19 +01:00
```javascript
import { launch } from 'cloakbrowser/puppeteer' ;
const browser = await launch ();
const page = await browser . newPage ();
await page . goto ( 'https://protected-site.com' );
console . log ( await page . title ());
await browser . close ();
```
### Options
```javascript
2026-03-04 19:23:16 +01:00
import { launch , launchContext , launchPersistentContext } from 'cloakbrowser' ;
2026-02-23 19:57:19 +01:00
// With proxy
const browser = await launch ({
proxy : 'http://user:pass@proxy:8080' ,
});
2026-03-04 20:11:30 +01:00
// With proxy object (bypass, separate auth fields)
const browser = await launch ({
proxy : { server : 'http://proxy:8080' , bypass : '.google.com' , username : 'user' , password : 'pass' },
});
2026-02-23 19:57:19 +01:00
// Headed mode (visible browser window)
const browser = await launch ({ headless : false });
// Extra Chrome args
const browser = await launch ({
2026-03-03 21:19:34 +01:00
args : [ '--fingerprint=12345' ],
2026-02-23 19:57:19 +01:00
});
2026-03-10 05:36:15 +01:00
// With timezone and locale
2026-03-01 01:07:11 +01:00
const browser = await launch ({
timezone : 'America/New_York' ,
locale : 'en-US' ,
});
// Auto-detect timezone/locale from proxy IP (requires: npm install mmdb-lib)
const browser = await launch ({
proxy : 'http://proxy:8080' ,
geoip : true ,
});
2026-03-10 05:36:15 +01:00
// Browser + context in one call (timezone/locale set via binary flags)
2026-02-23 19:57:19 +01:00
const context = await launchContext ({
userAgent : 'Custom UA' ,
viewport : { width : 1920 , height : 1080 },
locale : 'en-US' ,
2026-03-05 02:13:46 +01:00
timezone : 'America/New_York' ,
2026-02-23 19:57:19 +01:00
});
2026-03-04 19:23:16 +01:00
2026-03-04 20:11:30 +01:00
// Persistent profile — stay logged in, bypass incognito detection, load extensions
2026-03-04 19:23:16 +01:00
const ctx = await launchPersistentContext ({
userDataDir : './chrome-profile' ,
headless : false ,
proxy : 'http://user:pass@proxy:8080' ,
});
const page = ctx . pages ()[ 0 ] || await ctx . newPage ();
await page . goto ( 'https://example.com' );
await ctx . close (); // profile saved — reuse same path to restore state
2026-02-23 19:57:19 +01:00
```
2026-03-01 01:07:11 +01:00
### Auto Timezone/Locale from Proxy IP
When using a proxy, antibot systems check that your browser's timezone and locale match the proxy's location. Install `mmdb-lib` to enable auto-detection from an offline GeoIP database (~70 MB, downloaded on first use):
```bash
npm install mmdb-lib
```
```javascript
// Auto-detect — timezone and locale set from proxy's IP geolocation
const browser = await launch ({ proxy : 'http://proxy:8080' , geoip : true });
// Works with launchContext too
const context = await launchContext ({ proxy : 'http://proxy:8080' , geoip : true });
// Explicit values always win over auto-detection
const browser = await launch ({ proxy : 'http://proxy:8080' , geoip : true , timezone : 'Europe/London' });
```
> **Note:** For rotating residential proxies, the DNS-resolved IP may differ from the exit IP. Pass explicit `timezone`/`locale` in those cases.
2026-03-11 03:53:39 +01:00
### CLI
Pre-download the binary or check installation status from the command line:
```bash
npx cloakbrowser install # Download binary with progress output
npx cloakbrowser info # Show version, path, platform
npx cloakbrowser update # Check for and download newer binary
npx cloakbrowser clear-cache # Remove cached binaries
```
2026-02-23 19:57:19 +01:00
### Utilities
```javascript
2026-02-25 19:20:57 +01:00
import { ensureBinary , clearCache , binaryInfo , checkForUpdate } from 'cloakbrowser' ;
2026-02-23 19:57:19 +01:00
// Pre-download binary (e.g., during Docker build)
await ensureBinary ();
// Check installation
console . log ( binaryInfo ());
// Force re-download
clearCache ();
2026-02-25 19:20:57 +01:00
// Manually check for newer Chromium version
const newVersion = await checkForUpdate ();
if ( newVersion ) console . log ( `Updated to ${ newVersion } ` );
2026-02-23 19:57:19 +01:00
```
## Test Results
| Detection Service | Stock Browser | CloakBrowser |
|---|---|---|
| **reCAPTCHA v3** | 0.1 (bot) | **0.9** (human) |
| **Cloudflare Turnstile** | FAIL | **PASS** |
| **FingerprintJS** | DETECTED | **PASS** |
| **BrowserScan** | DETECTED | **NORMAL** (4/4) |
| **bot.incolumitas.com** | 13 fails | **1 fail** |
| `navigator.webdriver` | `true` | ** `false` ** |
2026-03-05 03:41:36 +01:00
| CDP detection | Detected | **Not detected** |
| TLS fingerprint | Mismatch | **Identical to Chrome** |
| | | **Tested against 30+ detection sites** |
2026-02-23 19:57:19 +01:00
## Configuration
| Env Variable | Default | Description |
|---|---|---|
| `CLOAKBROWSER_BINARY_PATH` | — | Skip download, use a local Chromium binary |
| `CLOAKBROWSER_CACHE_DIR` | `~/.cloakbrowser` | Binary cache directory |
2026-02-27 02:56:49 +01:00
| `CLOAKBROWSER_DOWNLOAD_URL` | `cloakbrowser.dev` | Custom download URL |
2026-02-25 19:20:57 +01:00
| `CLOAKBROWSER_AUTO_UPDATE` | `true` | Set to `false` to disable background update checks |
2026-03-02 18:20:57 +01:00
| `CLOAKBROWSER_SKIP_CHECKSUM` | `false` | Set to `true` to skip SHA-256 verification after download |
2026-02-23 19:57:19 +01:00
## Migrate From Playwright
```diff
- import { chromium } from 'playwright';
- const browser = await chromium.launch();
+ import { launch } from 'cloakbrowser';
+ const browser = await launch();
const page = await browser.newPage();
// ... rest of your code works unchanged
```
## Platforms
2026-03-05 03:41:36 +01:00
| Platform | Chromium | Patches | Status |
|---|---|---|---|
| Linux x86_64 | 145 | 26 | ✅ Latest |
| macOS arm64 (Apple Silicon) | 145 | 26 | ✅ Latest |
| macOS x86_64 (Intel) | 145 | 26 | ✅ Latest |
| Windows x86_64 | 145 | 26 | ✅ Latest |
2026-02-25 19:20:57 +01:00
2026-02-23 19:57:19 +01:00
## Requirements
- Node.js >= 18
- One of: `playwright-core` >= 1.40 or `puppeteer-core` >= 21
2026-03-01 06:10:23 +01:00
## Troubleshooting
2026-03-04 20:11:30 +01:00
**Site detects incognito / private browsing mode**
By default, `launch()` opens an incognito context. Some sites (like BrowserScan) detect this. Use `launchPersistentContext()` instead — it runs with a real user profile:
```javascript
import { launchPersistentContext } from 'cloakbrowser' ;
const ctx = await launchPersistentContext ({
userDataDir : './my-profile' ,
headless : false ,
});
```
This also gives you cookie and localStorage persistence across sessions.
2026-03-01 06:10:23 +01:00
**reCAPTCHA v3 scores are low (0.1– 0.3)**
Avoid `page.waitForTimeout()` — it sends CDP protocol commands that reCAPTCHA detects. Use native sleep instead:
```javascript
// Bad — sends CDP commands, reCAPTCHA detects this
await page . waitForTimeout ( 3000 );
// Good — invisible to the browser
await new Promise ( r => setTimeout ( r , 3000 ));
```
Other tips for maximizing reCAPTCHA scores:
- **Use Playwright, not Puppeteer** — Puppeteer sends more CDP protocol traffic that reCAPTCHA detects ([details ](#puppeteer ))
- **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** (`--fingerprint=12345` ) for consistent device identity across sessions
2026-03-05 03:41:36 +01:00
- **Use `page.type()` instead of `page.fill()` ** for form filling — `fill()` sets values directly without keyboard events, which reCAPTCHA's behavioral analysis flags. `type()` with a delay simulates real keystrokes:
```javascript
await page.type('#email', 'user@example.com', { delay: 50 });
` ``
2026-03-01 06:10:23 +01:00
- **Minimize ` page.evaluate()` calls** before the reCAPTCHA check fires — each one sends CDP traffic
2026-03-05 03:41:36 +01:00
**New update broke something? Roll back to the previous version**
When auto-update downloads a newer binary, the previous version stays in ` ~/.cloakbrowser/`. Point ` CLOAKBROWSER_BINARY_PATH` to the older cached binary:
` ``bash
# Linux
2026-03-07 02:28:53 +01:00
export CLOAKBROWSER_BINARY_PATH=~/.cloakbrowser/chromium-145.0.7632.159.2/chrome
2026-03-05 03:41:36 +01:00
# macOS
2026-03-05 05:53:51 +01:00
export CLOAKBROWSER_BINARY_PATH=~/.cloakbrowser/chromium-145.0.7632.109.2/Chromium.app/Contents/MacOS/Chromium
2026-03-05 03:41:36 +01:00
# Windows
2026-03-05 05:53:51 +01:00
set CLOAKBROWSER_BINARY_PATH=%USERPROFILE%\.cloakbrowser\chromium-145.0.7632.109.2\chrome.exe
2026-03-05 03:41:36 +01:00
` ``
2026-02-27 02:56:49 +01:00
## Links
- 🌐 [Website ](https://cloakbrowser.dev )
- 🐛 [Bug reports & feature requests ](https://github.com/CloakHQ/CloakBrowser/issues )
- 📦 [PyPI (Python package) ](https://pypi.org/project/cloakbrowser/ )
- 📖 [Full documentation ](https://github.com/CloakHQ/CloakBrowser#readme )
- 📧 Contact: cloakhq@pm .me
2026-02-23 19:57:19 +01:00
## License
2026-03-03 08:07:51 +01:00
- **Wrapper code** (this repository) — MIT. See [LICENSE ](https://github.com/CloakHQ/CloakBrowser/blob/main/LICENSE ).
- **CloakBrowser binary** (compiled Chromium) — free to use, no redistribution. See [BINARY-LICENSE.md ](https://github.com/CloakHQ/CloakBrowser/blob/main/BINARY-LICENSE.md ).
2026-03-04 22:40:25 +01:00
Use against financial, banking, healthcare, or government authentication systems without authorization is expressly prohibited.