How to Solve Cloudflare With Node.js (Puppeteer + Scrapeless)
Advanced Bot Mitigation Engineer
TL;DR:
- Cloudflare scores the browser, not the visitor. It reads fingerprint consistency, behavioral signals, and the exit IP, then sets a
cf_clearancecookie for a browser it trusts β so clearing it in Node.js means being a trusted browser, not answering a puzzle. - A local Node.js browser fails that scoring on three axes: the
navigator.webdrivertell, a headless-default fingerprint, and a datacenter exit IP. Stacking stealth plugins to patch all three is a maintenance treadmill. - The Scrapeless Scraping Browser clears the challenge during render β real self-developed Chromium, a consistent per-session fingerprint, and residential egress in 195+ countries, reached over one WebSocket endpoint.
- Your Node.js stays standard Puppeteer. Connect with
puppeteer.connect(), wait for the post-challenge content, and read the page β the only change is the connection URL. - Verified live: a Puppeteer session over the cloud browser cleared the Cloudflare challenge page, read the success marker
You bypassed the Cloudflare challenge! :D, and received acf_clearancecookie. - Free to start. New Scrapeless accounts include free Scraping Browser runtime β sign up at app.scrapeless.com.
Introduction: clearing Cloudflare in Node.js is a browser problem
A Cloudflare challenge is not an image CAPTCHA you decode. 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 reframes the Node.js task. There is no answer to submit β the work is presenting a browser Cloudflare already trusts. A local headless Chromium driven by Puppeteer can't: it announces automation through the navigator.webdriver property, ships headless-default fonts and canvas behavior, and exits from an IP reputation services already know. You can bolt on a stealth plugin and then keep patching as Chromium and the detectors move. This guide takes the shorter path: drive the Scrapeless Scraping Browser from standard Puppeteer, so the fingerprint, behavior, and exit IP are handled server-side and the challenge clears during a normal render.
What Cloudflare actually checks
Cloudflare's own documentation describes a verification step that runs without interrupting the visitor. In practice it grades three things and grants a cf_clearance cookie β a standard HTTP cookie β when they pass:
| What Cloudflare reads | Why a local Node.js browser fails it |
|---|---|
| Fingerprint consistency | headless defaults and the webdriver flag contradict a real Chrome |
| Behavioral signals | scripted timing without a coherent browser surface |
| Exit IP reputation | datacenter IPs score worse than residential ones |
A coherent, trusted browser matters more than any single evasion β which is why moving all three server-side is more durable than stacking stealth plugins.
Why Scrapeless Scraping Browser
The Scrapeless Scraping Browser is a customizable, anti-detection cloud browser designed for web crawlers and AI agents. For Cloudflare specifically, it brings:
- Real self-developed Chromium that runs the challenge JavaScript exactly like Chrome, so it resolves instead of stalling.
- A consistent per-session fingerprint β no
navigator.webdrivertell, no headless defaults leaking through. - Residential egress in 195+ countries β Cloudflare 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
puppeteer-coreinstalled (no bundled Chromium needed β you connect to a remote one)- A Scrapeless account and API key β sign up at app.scrapeless.com
Full connection details live in the Scraping Browser documentation.
Install
You connect to a remote browser, so puppeteer-core (no bundled Chromium) is all you need.
bash
npm install puppeteer-core
export SCRAPELESS_API_KEY=your_api_token_here # free key at https://app.scrapeless.com
Connect and clear the challenge
Build the endpoint, connect with puppeteer.connect(), navigate, and wait for the post-challenge content to attach. The challenge is solved during render β there is no separate solve call.
javascript
import puppeteer from "puppeteer-core";
import { URLSearchParams } from "node:url";
const params = new URLSearchParams({
token: process.env.SCRAPELESS_API_KEY,
sessionTTL: "180",
proxyCountry: "US",
});
const endpoint = `wss://browser.scrapeless.com/api/v2/browser?${params}`;
const TARGET = "https://www.scrapingcourse.com/cloudflare-challenge";
const browser = await puppeteer.connect({ browserWSEndpoint: endpoint });
const page = await browser.newPage();
await page.goto(TARGET, { waitUntil: "domcontentloaded", timeout: 90000 });
// The cloud browser clears Cloudflare during render; wait for the real content.
await page.waitForSelector("#challenge-title", { timeout: 90000 });
console.log("cleared:", await page.$eval("#challenge-title", (el) => el.innerText));
const cookies = await page.cookies();
console.log("cf_clearance:", cookies.some((c) => c.name === "cf_clearance"));
await browser.close();
A live run of this script printed cleared: You bypassed the Cloudflare challenge! :D and cf_clearance: true β standard Puppeteer, sourced through the cloud browser.
Get your API key on the free plan: app.scrapeless.com
Confirm the pass and carry the clearance
The reliable signal is the real content attaching β once the post-challenge element is present, the page cleared. Cloudflare also sets a cf_clearance cookie on the passing session, which you can read to carry the clearance to other requests in the same session. The W3C WebDriver specification requires conforming automation to expose the webdriver flag; the cloud browser does not carry it, which is why the first check reads clean.
javascript
const cookies = await page.cookies();
const clearance = cookies.find((c) => c.name === "cf_clearance");
console.log("cf_clearance present:", Boolean(clearance));
if (clearance) console.log("domain:", clearance.domain);
Pin the region for a reliable clear
Cloudflare scores the exit IP, so pin proxyCountry to a residential region. Turn it to any ISO country code.
javascript
const params = new URLSearchParams({
token: process.env.SCRAPELESS_API_KEY,
sessionTTL: "180",
proxyCountry: "US", // or "DE", "JP", "ANY"
});
Where local Node.js browsers stop
A local Puppeteer scraper is fine until Cloudflare starts scoring the browser. Then the headless fingerprint, the webdriver flag, and the datacenter IP each fail a different check, and the interstitial replaces the content. Those are fingerprint, behavior, and IP problems β the three the cloud browser handles server-side. Connecting to Scrapeless hands them to a managed session while your Puppeteer code stays standard.
What you get back
The session behaves like any Puppeteer session β page.goto, page.waitForSelector, page.content(), and cookies all work. A few honest observations from clearing Cloudflare over the cloud browser:
navigator.webdriveris absent, so the first check Cloudflare runs reads like a real Chrome.- The exit IP matches
proxyCountryβ a residential region clears far more reliably than a datacenter IP. 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 Cloudflare, keep your Node.js
Clearing Cloudflare in Node.js is not about decoding a challenge β it is about presenting a browser Cloudflare trusts. The Scrapeless Scraping Browser handles the three things it scores (fingerprint, behavior, exit IP) server-side, so your Puppeteer code changes only its connection URL. Pin proxyCountry to a residential region, wait for the post-challenge content, and read cf_clearance to confirm the pass. For running the same cloud browser from Python instead, see the Scrapling production-scraper guide, and compare plans on the Scrapeless pricing page.
Ready to Build Your Cloudflare-Clearing Pipeline?
Join our community to claim a free plan and connect with developers scraping Cloudflare-protected sites: Discord Β· Telegram.
Sign up at app.scrapeless.com for free Scraping Browser runtime and point Puppeteer at the cloud browser that clears Cloudflare during render.
FAQ
Q: Do I need a separate CAPTCHA-solving service?
Not on a passing browser. Cloudflare 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 step to wire up.
Q: puppeteer or puppeteer-core?
Use puppeteer-core. It skips the bundled Chromium download because you connect to the cloud browser instead of launching a local one.
Q: Do I need a proxy?
No. Residential egress is built in β set proxyCountry to a region or ANY. Cloudflare 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 clearing Cloudflare legal?
Accessing publicly available data is generally permitted, but the rules vary by jurisdiction and site. Review the target's terms of service, respect robots directives, and consult counsel for anything sensitive.
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.



