How to Solve Cloudflare Turnstile When Web Scraping
Specialist in Anti-Bot Strategies
TL;DR:
- Cloudflare Turnstile scores the browser, not the user. It reads fingerprint consistency, behavioral signals, and the exit IP in the background, then issues a
cf_clearancecookie to a browser it trusts — so the fix is being a trusted browser, not answering a puzzle. - A local headless browser fails Turnstile on three axes at once: the
navigator.webdrivertell, headless-default fingerprint, and a datacenter exit IP. Patching all three by hand is a maintenance treadmill. - The Scrapeless Scraping Browser clears Turnstile during render — real self-developed Chromium, a consistent per-session fingerprint, and residential egress in 195+ countries, reached over one WebSocket endpoint.
- Your automation code is unchanged. It speaks CDP, so
puppeteer.connect()andchromium.connectOverCDP()work as-is; you point the connection at the cloud browser and read the post-challenge DOM. - Turnstile ships in three modes — non-interactive, invisible, and managed checkbox — and all three end in the same
cf_clearancegrant on a passing browser. - Free to start. New Scrapeless accounts include free Scraping Browser runtime — sign up at app.scrapeless.com.
Introduction: Turnstile scores the browser, not a puzzle
Cloudflare Turnstile is not an image CAPTCHA. It runs in the background and grades the browser that loaded the page: whether the fingerprint is internally consistent, whether the interaction signals look human, and whether the exit IP has a clean reputation. When the score passes, Cloudflare sets a cf_clearance cookie and the real page loads. When it doesn't, you get an interstitial instead of content.
That changes what "solving Turnstile" means. There is no answer to submit — the work is presenting a browser Cloudflare already trusts. A local headless Chromium can't: it announces automation through the navigator.webdriver property, ships with headless-default fonts and canvas behavior, and exits from an IP reputation services already know. You can patch each of those locally and then keep patching as Chromium and the detectors change.
This guide takes the shorter path: drive the Scrapeless Scraping Browser — an anti-detection cloud browser — with the Puppeteer or Playwright you already use, so the fingerprint, behavioral surface, and exit IP are handled server-side and Turnstile clears during a normal render.
What Turnstile actually checks
Cloudflare's Turnstile documentation describes a widget that verifies visitors without interrupting them. In practice it ships in three modes, and the same cloud browser handles all of them because it presents a coherent real browser rather than a patched one:
| Mode | What the visitor sees | What it scores |
|---|---|---|
| Non-interactive | a widget that verifies with no click | fingerprint + passive behavioral signals |
| Invisible | nothing rendered | pure background scoring |
| Managed | a "Verify you are human" checkbox | the above plus an interaction event |
All three resolve to a cf_clearance cookie — a standard HTTP cookie — set on a browser that passed. Turnstile also draws on Privacy Pass-style tokens defined in the IETF Privacy Pass issuance protocol, which is why a coherent, trusted browser matters more than any single evasion.
Why Scrapeless Scraping Browser
The Scrapeless Scraping Browser is a customizable, anti-detection cloud browser designed for web crawlers and AI agents. For Turnstile specifically, it brings:
- Real self-developed Chromium that runs the widget's JavaScript exactly like Chrome, so the challenge resolves instead of stalling.
- A consistent per-session fingerprint — no
navigator.webdrivertell, no headless defaults leaking through. - Residential egress in 195+ countries — Turnstile scores the exit IP, and a residential region reads clean where a datacenter IP does not.
- Session persistence so a
cf_clearancegrant can be reused across requests in the same session. - One connection surface — every option is a query parameter on the same WebSocket endpoint.
Get your API key on the free plan at app.scrapeless.com.
Prerequisites
- Node.js 18 or newer
- A Scrapeless account and API key — sign up at app.scrapeless.com
- Basic familiarity with Puppeteer or Playwright and the terminal
The examples below are verified against the Scraping Browser's documented CDP surface. Running them end to end needs a live Scrapeless session; add your API key to execute each one. Full connection details live in the Scraping Browser documentation.
Install
bash
npm install puppeteer-core # or: playwright-core
export SCRAPELESS_API_KEY=sk_... # free key at https://app.scrapeless.com
Configure the connection
Everything is a query parameter on wss://browser.scrapeless.com/api/v2/browser. Pin proxyCountry to a residential region, since Turnstile scores the exit IP.
javascript
import puppeteer from "puppeteer-core";
const params = new URLSearchParams({
token: process.env.SCRAPELESS_API_KEY,
sessionTTL: "180",
proxyCountry: "US",
});
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://browser.scrapeless.com/api/v2/browser?${params}`,
});
Clear Turnstile and read the token
The Scrapeless Scraping Browser solves Turnstile automatically during render — there is no explicit solve call to make. You navigate, then wait for the Turnstile token to appear, which is the signal the widget resolved. Subsequent scraping is up to you.
javascript
import puppeteer from "puppeteer-core";
const TARGET = "https://www.scrapingcourse.com/cloudflare-challenge";
const params = new URLSearchParams({
token: process.env.SCRAPELESS_API_KEY,
sessionTTL: "180",
proxyCountry: "US",
});
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://browser.scrapeless.com/api/v2/browser?${params}`,
});
const page = await browser.newPage();
await page.goto(TARGET, { waitUntil: "domcontentloaded", timeout: 60000 });
// The browser auto-solves Turnstile; wait for the token to confirm it resolved.
await page.waitForFunction(
() => window.turnstile && window.turnstile.getResponse(),
{ timeout: 60000 }
);
const token = await page.evaluate(() => window.turnstile.getResponse());
console.log("turnstile token ->", token.slice(0, 24) + "...");
console.log("title ->", await page.title());
await browser.close();
Get your API key on the free plan: app.scrapeless.com
The same flow in Playwright
Playwright connects to the same cloud browser over CDP; the session auto-solves Turnstile during render, and you wait for the token the same way.
javascript
import { chromium } from "playwright-core";
const params = new URLSearchParams({
token: process.env.SCRAPELESS_API_KEY,
sessionTTL: "180",
proxyCountry: "US",
});
const browser = await chromium.connectOverCDP(
`wss://browser.scrapeless.com/api/v2/browser?${params}`
);
const page = await browser.newPage();
await page.goto("https://www.scrapingcourse.com/cloudflare-challenge", {
waitUntil: "domcontentloaded",
});
await page.waitForFunction(() => window.turnstile && window.turnstile.getResponse());
console.log("title ->", await page.title());
await browser.close();
Confirm the pass
The reliable signal is the Turnstile token — once window.turnstile.getResponse() returns a value, the widget resolved and you can proceed. Cloudflare also sets a cf_clearance cookie on the passing session, which you can read if you need to carry the clearance to other requests.
javascript
const token = await page.evaluate(() => window.turnstile && window.turnstile.getResponse());
console.log("turnstile token present ->", Boolean(token));
const cookies = await page.cookies();
console.log("cf_clearance present ->", cookies.some((c) => c.name === "cf_clearance"));
What you get back
- The
navigator.webdriversignal is absent — the cloud browser does not announce automation, so the first check Turnstile runs reads like a real Chrome. - The exit IP matches
proxyCountry— a residential region clears far more reliably than a datacenter IP, which Turnstile scores down. cf_clearanceis present after a pass — reuse the same session to carry the grant across follow-up requests.- Warm the session for stubborn pages — load the site's homepage first in the same session before the protected page, so the behavioral surface looks like a normal visit.
Conclusion: clear Turnstile, keep your code
Solving Turnstile is not about answering a challenge — it is about presenting a browser Cloudflare trusts. The Scrapeless Scraping Browser handles the three things Turnstile scores (fingerprint, behavior, exit IP) server-side, so your Puppeteer or Playwright code changes only its connection URL. Pin proxyCountry to a residential region, warm the session on the homepage, and read cf_clearance to confirm the pass. For the difference between the interstitial and the widget, see Cloudflare challenge vs Turnstile, and compare plans on the Scrapeless pricing page.
Ready to Build Your Turnstile-Clearing Pipeline?
Join our community to claim a free plan and connect with developers scraping Turnstile-protected sites: Discord · Telegram.
Sign up at app.scrapeless.com for free Scraping Browser runtime and point Puppeteer or Playwright at the cloud browser that clears Turnstile during render.
FAQ
Q: Is Turnstile a CAPTCHA I have to solve with a solver service?
Not on a passing browser. Turnstile mostly scores fingerprint, behavior, and IP in the background and issues a cf_clearance cookie. The cloud browser is built to pass that scoring during a normal render, so there is no separate puzzle to answer.
Q: Does it handle the invisible and non-interactive modes too?
Yes. All three Turnstile modes end in the same cf_clearance grant, and the managed session handles the passive scoring the same way it handles the checkbox variant.
Q: Do I need a proxy?
No. Residential egress is built in — set proxyCountry to a region or ANY. Turnstile scores the exit IP, so a residential region clears more reliably than a datacenter address.
Q: The challenge still shows on some pages — what helps?
Pin proxyCountry to a residential region and warm the session by loading the site's homepage first in the same session before the protected page, so the behavioral surface reads like a normal visit.
Q: Is my Puppeteer or Playwright code compatible?
Yes. The Scraping Browser speaks CDP, so puppeteer.connect() and chromium.connectOverCDP() work unchanged — you only change the connection URL.
Q: Is clearing Turnstile legal?
Access publicly available data in line with the site's terms and applicable law. These techniques are for authorized automation, testing, and research where you have permission.
At Scrapeless, we only access publicly available data while strictly complying with applicable laws, regulations, and website privacy policies. The content in this blog is for demonstration purposes only and does not involve any illegal or infringing activities. We make no guarantees and disclaim all liability for the use of information from this blog or third-party links. Before engaging in any scraping activities, consult your legal advisor and review the target website's terms of service or obtain the necessary permissions.



