🎯 A customizable, anti-detection cloud browser powered by self-developed Chromium designed for web crawlers and AI Agents.πŸ‘‰Try Now
Back to Blog

How to Solve Cloudflare With Node.js (Puppeteer + Scrapeless)

Ethan Brown
Ethan Brown

Advanced Bot Mitigation Engineer

09-Jul-2026

TL;DR:

  • Cloudflare scores the browser, not the visitor. It reads fingerprint consistency, behavioral signals, and the exit IP, then sets a cf_clearance cookie 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.webdriver tell, 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 a cf_clearance cookie.
  • 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.webdriver tell, 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_clearance grant 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-core installed (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 Copy
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 Copy
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 Copy
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 Copy
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.webdriver is 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_clearance is 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.

Most Popular Articles

Catalogue