Cloudflare Turnstile in 2026: what stealth plugins miss
You hit a page. No checkbox, no "pick all the buses", nothing to click. For a human the page just loads. For your scraper the page hangs, returns a 403, or comes back with the content missing and only the shell.
That is Turnstile. Not a CAPTCHA in the old sense. Cloudflare's newer challenge, runs in the background, decides if your request passes before the page finishes rendering. In 2026 it is on a lot more sites than a year ago: ticketing, classifieds, e-commerce, dev tools, basically anything behind Cloudflare Pro or higher.
This post is about what Turnstile is, how it is different from the old JS challenge, what signals it reads, why most Puppeteer-stealth setups stopped working around Q4 2025, and what you can do about it in 2026. For the full Cloudflare playbook see the pillar on bypass Cloudflare bot protection.
What Turnstile actually is
Turnstile is what Cloudflare put in place of the old "checking your browser" interstitial and of third-party CAPTCHAs like reCAPTCHA and hCaptcha. It ships as a small JS widget site owners embed on sensitive pages (login, signup, checkout, search), or as an invisible mode that runs on every request going through a protected domain.
From the Turnstile docs:
The part that matters if you are building a scraper: Turnstile does not only check your browser. It checks your TLS handshake, your HTTP/2 frame order, your Client Hints, plus more signals that fire way before any JavaScript runs. If the handshake already looks off, Turnstile never even gets to the token step. You are out before you started.
How Turnstile is different from the old JS challenge
The pre-Turnstile "Cloudflare Managed Challenge" (the I'm-under-attack interstitial) was mostly JavaScript. You waited 5 seconds, a cookie got set, you were in. Headless browsers with a stealth plugin handled most of it because the challenge lived in JS land.
Turnstile pushed the hard checks earlier in the pipeline:
| Layer | Old JS challenge | Turnstile |
|---|---|---|
| TLS handshake | Not a primary signal | Primary signal (JA3/JA4) |
| HTTP/2 frame order | Ignored | Checked |
| Client Hints | Optional | Expected |
| JavaScript challenge | Blocking, visible | Runs in background, non-blocking |
| User interaction | Checkbox (managed) | None (invisible) |
| Server-side verification | Cookie | One-time token |
Why this matters: every layer Turnstile moved *before* JavaScript is a layer where a headless browser buys you nothing. Chrome's TLS handshake looks like Chrome because Chrome makes it. Puppeteer's handshake looks like the Go or Node HTTP client underneath, which does not look like Chrome.
The signals Turnstile reads
From traffic we reverse-engineered and from public research, these are the signals Turnstile leans on most:
1. JA3 / JA4 TLS fingerprint. The ordered list of cipher suites, extensions, elliptic curves your client puts into the TLS ClientHello. Chrome 133 has a specific fingerprint. Python requests has a completely different one. Turnstile has the full table.
2. HTTP/2 SETTINGS frame. Chrome sends HTTP/2 SETTINGS in a specific order, with specific values. Go's net/http and Node's undici do not.
3. Client Hints (Sec-CH-UA, Sec-CH-UA-Platform, Sec-CH-UA-Mobile). Chrome sends these on every request. Your scraper probably does not, or sends values that do not match your User-Agent.
4. Canvas, WebGL, audio fingerprints. The classic browser fingerprints. Turnstile collects them but weighs them less than the network-layer ones, because they are easier to fake.
5. Mouse movement and timing (managed mode only). In the visible widget mode, Turnstile records how the cursor moved toward the widget before you clicked.
6. Behavioral history. If the IP or ASN has recent Turnstile failures, the bar goes up. Residential IPs with clean history pass easier than datacenter IPs with abuse records.
You cannot fake all of these from inside a headless browser without patching a lot. The TLS fingerprint in particular is decided at the OS or Node/Go runtime level, below Chromium. The stealth plugin does not touch that layer.
Why Puppeteer-stealth stopped working
Through 2023 and most of 2024, puppeteer-extra-plugin-stealth was the default fix for Cloudflare. It patched around 20 browser-fingerprint leaks (the WebDriver flag, navigator.plugins, Notification permission, and so on) and it worked because Cloudflare was mostly reading browser-level signals.
In late 2025 Cloudflare moved a lot of the weight to network-level signals. The stealth plugin still patches what it always patched, but Turnstile does not use those signals as the first filter anymore. It reads your TLS fingerprint first. If your Puppeteer instance runs the bundled Chromium on Linux in Docker, your handshake is technically Chrome, but your HTTP/2 settings and Client Hints usually end up mangled by whatever process is driving it.
The symptom you see: your old Puppeteer-stealth scraper works on page 1 of a site, dies on page 3 where a Turnstile widget is embedded, and you cannot tell why, because the error is just a 403 with no visible challenge at all.
Adding another stealth plugin does not help. The miss is below Chromium, not inside it. Browser-level masking loses against network-level fingerprinting. To fix this you need to control the TLS layer directly, not patch more of the browser.
What actually works in 2026
The three approaches that are still reliable today, from cheap to expensive:
1. TLS-fingerprinted HTTP client, no browser. A Rust or Go client that produces a Chrome-accurate JA4 hash, sends HTTP/2 SETTINGS in the right order, and includes matching Client Hints. Around 20x faster than a browser. Passes most Turnstile-protected pages that do not need JS to render.
2. TLS-fingerprinted HTTP plus an external Turnstile solver for the small slice of sites that require a real Turnstile token. The fetch still happens from a fingerprinted client, the token is solved by a service (Capsolver, 2Captcha, Parallax) and injected.
3. Full browser, only as last resort. Chrome over CDP on a real residential IP, for sites that actually need JS execution on top of a Turnstile widget. Slow, expensive, brittle. For most Turnstile pages this is overkill.
From what we see on webclaw traffic: approach 1 covers about 80% of Turnstile-protected URLs. Approach 2 picks up another 15%. Approach 3 is the last 5%, mostly ticketing and high-value classifieds.
Handling Turnstile with webclaw
webclaw routes every request through this stack for you. Default path is a TLS-fingerprinted fetch. If the response looks like a Turnstile challenge (we check for the challenge marker in the body), it falls through to the solver plus a fingerprinted retry. If that one fails too, it falls through to a real browser session.
Scraping a Turnstile-protected page with the Python SDK:
from webclaw import WebclawClient
client = WebclawClient(api_key="YOUR_API_KEY")
result = client.scrape(
url="https://shop.example.com/products/123",
format="llm",
)
print(result.markdown)No stealth plugin. No Chrome install. No solver configuration. The routing happens on our side. If the page needed a Turnstile token, it got solved. The response you see is the real page, not a challenge screen.
Same call from JavaScript:
import { Webclaw } from "@webclaw/sdk";
const client = new Webclaw({ apiKey: process.env.WEBCLAW_API_KEY });
const { markdown } = await client.scrape({
url: "https://shop.example.com/products/123",
format: "llm",
});From Go:
client := webclaw.NewClient("YOUR_API_KEY")
result, err := client.Scrape(ctx, &webclaw.ScrapeRequest{
URL: "https://shop.example.com/products/123",
Format: "llm",
})Full endpoint reference in the scrape API docs.
Start on the free tier or grab an API key if you already have an account.
When you still need a real browser
Turnstile by itself does not force you into a full browser. A page does force one when:
For those, webclaw's agent mode drives a real Chrome over CDP with a residential exit. Slower than the fingerprinted path, but needed in around 5% of cases.
Simple rule: try the fingerprinted fetch first. Only go to a browser when the markdown you get back is missing a section you can clearly see in your own browser.
Comparing approaches
| Approach | Handles Turnstile | Handles JS content | Latency | Cost per 1k |
|---|---|---|---|---|
curl / requests / fetch | No | No | Fast | Free |
curl-cffi / tls-client direct | Partial | No | Fast | Free + infra |
| Puppeteer-stealth | Unreliable in 2026 | Yes | Slow | Infra + residential |
| Puppeteer + external solver | Yes, slow | Yes | Slow | Infra + solver fees |
| webclaw fingerprinted path | Yes | No (fallback when needed) | Fast | Included |
| webclaw browser fallback | Yes | Yes | Slow | Included, higher credit cost |
For a broader landscape view see Best web scraping APIs for LLMs in 2026.
Frequently asked questions
What is Cloudflare Turnstile?
Turnstile is Cloudflare's non-interactive challenge system. It replaced the old "checking your browser" interstitial and the third-party CAPTCHAs. Runs fingerprinting and behavioral checks in the background, issues a one-time token the server then verifies.
Can Puppeteer-stealth bypass Turnstile in 2026?
Not reliably. Turnstile's main signals sit below the browser layer (TLS fingerprint, HTTP/2 frame order, Client Hints) and the stealth plugin does not patch any of that. Works on some sites, fails silently on others, and the failure looks just like a regular network error.
How do I detect if a page is using Turnstile?
Look for a cf-turnstile div or a https://challenges.cloudflare.com/turnstile/ script tag in the HTML. If the initial response is small and contains one of those, Turnstile is on. Invisible mode is harder to catch, but if your request returns a 403 with CF-Ray headers and no visible challenge, treat it as Turnstile.
Does webclaw solve Turnstile?
Yes, as part of the routing pipeline. A TLS-fingerprinted fetch handles most Turnstile pages without even solving a token. For the subset that needs a real token, the pipeline routes through a solver plus retry. You call /v1/scrape and get back clean markdown either way. More on how the bypass works.
What is the difference between JA3 and JA4 fingerprints?
JA3 is the older TLS fingerprint, a hash of the ClientHello field values. JA4 is the newer version that also captures HTTP/2 metadata and ALPN negotiation. Harder to fake because it looks at more of the handshake. Cloudflare uses both. The hard target is a Chrome-accurate JA4.
Does Turnstile work without JavaScript?
The widget itself needs JavaScript to run the non-interactive challenge and produce a token. But the site can still block you at the TLS layer before any JS runs. That is why HTTP-only bypasses sometimes work: the JS check never fires because the TLS fingerprint already got you through, and the challenge widget was just an extra layer.
Is solving Turnstile legal?
Depends on your jurisdiction and the terms of service. Bypassing access controls on a site you are not authorized to scrape is normally a ToS breach and can become a CFAA issue in the US. Scraping public data on sites that allow it, or sites you own, is fine. For your specific case talk to a lawyer, not to a blog post.
How often does Cloudflare update Turnstile?
Often. The public version ships quietly, sometimes weekly. What worked in January can stop working in April. This is why any scraping stack built around one bypass trick breaks in the end. A layered router that can escalate from fingerprinted fetch to solver to browser is more robust than any single method.
Can I use a residential proxy to bypass Turnstile?
A residential IP helps with the IP reputation signal, which is one of Turnstile's signals. It does nothing for TLS or HTTP/2 fingerprint mismatches. Residential IP plus TLS-fingerprinted client together is stronger than either on its own.
What about Puppeteer-extra with `puppeteer-real-browser`?
Better than vanilla stealth against Turnstile because it uses a real installed Chrome instead of the bundled Chromium, so the TLS handshake is closer to the real thing. Still slow, still expensive, still has CDP leaks on some CF configs. Fine as a fallback layer, not great as the default.
Ready to ship past Turnstile? Get an API key or read the scrape docs. Already building with LLMs? See the LangChain and LlamaIndex guides for plugging webclaw into your RAG pipeline.
Read next: Bypass Cloudflare bot protection | Web scraping for AI agents | MCP and web scraping