Skip to content
Troubleshooting

Playwright blocked by Cloudflare — what to do

Your Playwright script gets past navigation but hits a Cloudflare interstitial — either the managed-challenge page, a Turnstile widget that fails silently, or a 403 returned by the protected endpoint after a few requests.

Cause

Cloudflare scores three layers: TLS fingerprint (JA3/JA4), JS challenge timing, and browser entropy (canvas, WebGL, hardware concurrency). Default Playwright sessions trip at least one signal within a couple of requests. Even with stealth plugins the captcha layer still scores.

Fix

Pull the Turnstile token via Sonic and inject it before submitting any form or hitting any protected endpoint. The page never sees Playwright trying to solve the widget, so the score never tanks.

Runnable example
from playwright.async_api import async_playwright
from captchasonic import CaptchaSonic

async with async_playwright() as p:
    browser = await p.chromium.launch()
    page = await browser.new_page()
    await page.goto('https://target.example.com/login')

    sonic = CaptchaSonic('YOUR_API_KEY')
    token = sonic.solve(
        type='TurnstileTaskProxyless',
        website_url=page.url,
        website_key='0x4AAAAAAA...',
    ).token

    await page.evaluate(
        "(t) => document.querySelector('[name=cf-turnstile-response]').value = t",
        token,
    )
    await page.click('form button[type=submit]')

Frequently asked questions

They help with the surrounding navigation (TLS fingerprint, JS timing). Token injection covers the captcha challenge specifically. Most production setups run both.

TurnstileTaskProxyless handles both the embedded widget and the managed-challenge interstitial. Pass the same parameters; Sonic detects the surface and routes accordingly.

Yes. Pass the proxy hostname / port / credentials in the task payload — Sonic solves from inside your egress IP so the token matches the fingerprint Cloudflare will see on subsequent requests.

Related guides