Why Is CloakBrowser Still Getting Blocked? 7 Source Code Fixes to Debug Detection

CloakBrowser gets blocked when stealth layers fall back to detectable paths such as missing CDP sessions, untrusted keyboard events with isTrusted === false, or malformed proxy configurations that force evaluate-based injection visible to page scripts.

CloakBrowser is an open-source automation framework designed to mask headless Chrome artifacts, yet developers often find CloakBrowser still getting blocked by advanced bot detection systems. Understanding the seven specific leak vectors in the CloakHQ/CloakBrowser source code—from CDP session failures in cloakbrowser/human/keyboard.py to proxy credential truncation—enables precise debugging and elimination of detection signals.

Start with this minimal safe configuration that avoids the common pitfalls described below:

from cloakbrowser import launch

browser = launch(
    headless=False,                # headless=True can add minor signals; set False for debugging

    stealth_args=True,             # keep default fingerprint flags

    locale="en-US",                # passed as binary flag (--lang=en-US)

    timezone="America/New_York",   # binary flag (--fingerprint-timezone=America/New_York)

    backend="playwright",          # default – provides a CDP session

    humanize=True,                 # apply human-like mouse/keyboard patches

    human_preset="default",        # or "careful" for slower, more realistic motions

)
page = browser.new_page()
page.goto("https://bot.incolumitas.com")
browser.close()

Source: launch definition in cloakbrowser/browser.py (lines 54-66) and the build_args helper (lines 389-447).

1. CDP Session Failures Force Evaluate-Based Injection

When the Chrome DevTools Protocol (CDP) session is unavailable, CloakBrowser falls back to page.evaluate for keyboard injection. This exposes the automation script to the page context because evaluate runs in the isolated "stealth world" that detection scripts can fingerprint via stack inspection.

According to the CloakHQ/CloakBrowser source code, this fallback occurs in cloakbrowser/human/keyboard.py at line 75 and in cloakbrowser/human/keyboard_async.py at line 36. When this happens, detection patterns scanning Error.stack for :302: (the line number signature of evaluated code) trigger blocks.

To verify if your launch is leaking evaluate calls, inject this detection hook:

await page.evaluate("""
    () => {
        window.__evaluateDetections = [];
        const orig = document.querySelector.bind(document);
        document.querySelector = function(sel) {
            try { throw new Error(); } catch (e) {
                if (e.stack && e.stack.includes(':302:')) {
                    window.__evaluateDetections.push(sel);
                }
            }
            return orig(sel);
        };
    }
""")

# Perform any action that might trigger a leak

await page.locator('#searchInput').click()
leaks = await page.evaluate('() => window.__evaluateDetections')
print("evaluate leaks:", leaks)   # Should be []

If the array contains entries, the fallback path was taken. Ensure you use a backend that provides a CDP session (playwright or patchright) and that no network issue blocks CDP creation.

2. Untrusted Keyboard Events (isTrusted === false)

Before issue #110, the human keyboard shim generated synthetic KeyboardEvent objects via page.evaluate, resulting in event.isTrusted === false. Sites checking this property reject the input as automated.

This logic resides in cloakbrowser/human/keyboard.py (line 75) and determines whether to use CDP or evaluate. To verify trusted event generation, type a shift-symbol and inspect the window properties:

await page.evaluate("""
    () => {
        window.__untrustedKeys = []; window.__trustedKeys = [];
        const inp = document.querySelector('#searchInput');
        inp.addEventListener('keydown', e => {
            (e.isTrusted ? window.__trustedKeys : window.__untrustedKeys).push(e.key);
        }, true);
    }
""")
await page.locator('#searchInput').click()
await page.keyboard.type('test!')
untrusted = await page.evaluate('() => window.__untrustedKeys')
trusted   = await page.evaluate('() => window.__trustedKeys')
print('untrusted:', untrusted)   # Should not contain '!'

print('trusted:',   trusted)     # Should contain '!'

Upgrade to v0.6.x or later and ensure humanize=True is set without custom config that disables the CDP path.

3. Locale and Timezone Set via CDP Instead of Binary Flags

Passing locale or timezone as Playwright context kwargs forces CDP-based Emulation.setLocale calls, which are detectable. The library deliberately sets these via binary flags only (--lang and --fingerprint-timezone).

In cloakbrowser/browser.py, the launch* functions (lines 81-85) and helper build_args (lines 389-447) handle this. Always provide these values through function arguments so they become binary flags, not CDP commands.

Verify your launch arguments include:

  • --lang=en-US
  • --fingerprint-timezone=America/New_York

If these are missing from the Chrome args but the locale is still set, the values are being applied via CDP and will be caught by detection scripts.

4. Malformed Proxy URLs Cause SOCKS5 Credential Truncation

Malformed proxy URLs lead Chromium to drop password characters (e.g., =), causing proxy rejection and fallback to less-stealthy modes. The normalization logic is in _resolve_proxy_config (lines 334-360) and _normalize_socks_string_url (lines 211-219).

Use URL-encoded credentials:

browser = launch(
    proxy="socks5://user:pa%3Dss@host:1080"  # encode special characters like '='

)

Or let the library normalize the string automatically.

5. Missing or Outdated Stealth Flags

The default stealth arguments from get_default_stealth_args are inserted only when stealth_args=True. Disabling them launches vanilla Chromium with detectable fingerprints.

In build_args (lines 389-447), the stealth_args flag gates insertion of default fingerprint flags. Keep stealth_args=True (the default). If you need custom flags, merge them with get_default_stealth_args() rather than replacing the list.

6. Incorrect Backend Selection (patchright vs playwright)

The patchright backend suppresses CDP signals (helpful for some reCAPTCHA v3 challenges) but breaks proxy authentication and add_init_script. Incorrect backend choice silently forces fallback to detectable paths.

The resolution logic is in _resolve_backend (lines 177-183). Use the default playwright backend unless specifically required. Set CLOAKBROWSER_BACKEND=playwright or pass backend="playwright" to launch to ensure CDP session availability.

7. Human-Behaviour Patch Not Applied

The humanize flag must be enabled to replace default Playwright keyboard and mouse actions with stealth implementations. Without it, the browser uses original Playwright implementations that lack trusted event generation.

The launch* functions (lines 39-45) call patch_browser or patch_browser_async only when humanize=True. Set this flag and optionally tune the preset:

browser = launch(
    humanize=True,
    human_preset="careful"  # slower, more realistic motions

)

Debugging Workflow: Six Steps to Isolate Detection Leaks

When CloakBrowser is still getting blocked, follow this systematic workflow to identify the exact vector:

  1. Run the built-in stealth test script. The examples/stealth_test.py file exercises live detection services (Botscan, Bot.incolumitas, Fingerprint Scan).

    python -m examples.stealth_test

    Review the pass/fail matrix; any "blocked" row indicates an active signal.

  2. Inject the evaluate detection hook as shown in the CDP section above to catch :302: stack traces.

  3. Verify keyboard event trust using the shift-symbol test to confirm isTrusted flags.

  4. Inspect the Chromium command line via debug logs. Enable logging to see the final argument list:

    import logging
    logging.basicConfig(level=logging.DEBUG)

    Look for --lang, --fingerprint-timezone, and properly escaped proxy credentials.

  5. Confirm the CDP session exists by checking browser._connection after launch. If None, the backend failed to create a session, forcing fallback paths.

  6. Re-run unit tests with pytest -k stealth after each configuration change to ensure detection hooks remain clean.

Summary

  • CDP session availability determines whether keyboard injection uses stealth CDP or detectable evaluate calls in cloakbrowser/human/keyboard.py.
  • Trusted events require humanize=True and version v0.6.x+ to avoid isTrusted === false detection.
  • Binary flags (--lang, --fingerprint-timezone) are stealthier than CDP emulation calls set via context kwargs.
  • Proxy URLs must be URL-encoded to prevent credential truncation in _resolve_proxy_config.
  • Stealth arguments must remain enabled via stealth_args=True and merged rather than replaced.
  • Backend selection should default to playwright unless specific patchright features are required.
  • Debugging workflow relies on the built-in stealth_test.py, detection hooks, and CDP session verification.

Frequently Asked Questions

Why does CloakBrowser pass some sites but fail others?

Different sites employ distinct detection vectors. One site may check event.isTrusted while another scans for CDP emissions. Use the evaluate detection hook and keyboard trust tests to identify which specific signal the failing site detects, then verify the corresponding configuration (CDP session, humanize flags, or locale settings) is correctly applied.

How do I verify that CDP is actually working and not falling back to evaluate?

Check browser._connection after launching; if it is None, no CDP session exists. Additionally, inject the stack-scanning hook that watches for :302: in Error.stack. If window.__evaluateDetections contains entries after interaction, the fallback path was taken, indicating CDP failure.

Should I use patchright or playwright as the backend?

Use playwright (the default) unless you specifically need to suppress CDP signals for reCAPTCHA v3 challenges. The patchright backend breaks proxy authentication and add_init_script, which can force detectable fallback behavior. Set backend="playwright" explicitly if unsure.

What is the correct way to set locale and timezone to avoid detection?

Always pass locale and timezone as arguments to launch(), not as Playwright context options. This ensures CloakBrowser sets them via binary flags (--lang and --fingerprint-timezone) rather than detectable CDP Emulation.setLocale calls. Verify these flags appear in the debug logs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →