How to Bypass Cloudflare with Playwright in 2026

On This Page How Cloudflare Detects Automation with PlaywrightApril 13, 2026 · 17 min read · Tool Comparison

How to Bypass Cloudflare with Playwright in 2026

Ever discharge up a Playwright test simply to hit Cloudflare & # 8217; s & # 8220; Checking your browser & # 8221; wall?

Your script work perfectly topically, but the moment they stir a Cloudflare-protected site, everything breaks.

CAPTCHAs appear, requests get blocked, and your CI/CD pipeline stalls.

I faced this exact issue screen an e-commerce platform.

My scripts kept failing, and I couldn & # 8217; t figure out why. Then I realized Cloudflare was noticeautomation signatures, browser fingermark, and bot-like navigation patternsthat Playwright leaves by nonpayment.

The solution?

Overview

Use Playwright in Cloudflare Workers

Cloudflare makes it possible to run Playwright right on the edge without provisioning servers or managing a browser substructure. Their Playwright distribution includes a headless Chromium representative that executes within the Workers runtime, which is utilitarian for tasks that require real webpage interpreting.

To get started, instal the Cloudflare & # 8217; # 145; specific library:

npm install -s @ cloudflare/playwright

Before deployment, the Worker must be configured right in wrangler.toml. A late compatibility escort is required, and a browser binding must be declared so the Worker can access the bundled browser:

compatibility_date = & # 8220; 2025-09-15 & # 8221;
compatibility_flags = [& # 8220; nodejs_compat & # 8221;]
browser = {binding = & # 8220; MYBROWSER & # 8221;}

Once set up, this enables features such as:

  • Full page rendering for visual capture or PDF output
  • Functional checks against UI constituent
  • Automated interaction flow fulfil close to the end user

Cloudflare besides offersPlaywright MCP, which permit AI agents via Workers AI operate browsers programmatically and exchange structured datum with web Page, expanding possibilities for agent & # 8217; # 145; driven automation on the edge.

In this guide, I & # 8217; ll demo you how to handle Cloudflare protection with stealth plugins, proxy rotation, fingermark spoofing, and human behavior simulation so your legitimate test automation runs smoothly.

How Cloudflare Detects Automation with Playwright

Cloudflare & # 8217; s bot catching isn & # 8217; t precisely checking if you & # 8217; re a browser or not. It & # 8217; s analyze 12 of signals to determine whether you & # 8217; re a real human or an automated script. Understanding these detection method is essential before you can configure Playwright to work around them.

1. Browser Fingerprinting

Cloudflare collects browser feature like canvas fingerprints, WebGL provide patterns, audio context signature, and font lists. Playwright & # 8217; s default configuration often produces fingerprints that are suspiciously consistent or match known automation patterns. Even minor inconsistency between what your browser claims to be and how it actually deport can actuate blocks.

Read More:

2. Automation Indicators

Out of the box, Playwright sets holding like navigator.webdriver to true, exposes automation-specific JavaScript objective, and leaves traces in the browser & # 8217; s Protocol. Cloudflare actively checks for these telling signs. If it finds window.chrome missing in a Chrome browser or detects Playwright-specific properties, you & # 8217; re swag instantly.

3. Behavioral Analysis

Existent users don & # 8217; t pilot websites like robots. Cloudflare monitors mouse movements, scroll patterns, keystroke dynamics, and timing between actions. Playwright script that click buttons outright, load pages without any shiner action, or navigate with inhuman precision stand out. The lack of natural pauses and erratic human behavior makes automated traffic obvious.

Cloudflare ’ s detection methods make it hard to predict how automation scripts will behave in real environments. Running Playwright test on BrowserStack lets you fulfil scripts on existent browsers and device, observe just how they interact with sites under realistic conditions, and identify points where automation triggers blocks.

4. Network and IP Reputation

Cloudflare tracks IP address and their reputation. If you & # 8217; re test from a datacenter IP, making hundreds of asking in quick succession, or showing inconsistent geolocation information, you & # 8217; ll trigger rate limits or outright blocks. Residential IPs and proper request pacing are essential to avoid this sensing layer.

Read More:

5. TLS and HTTP Fingerprinting

Beyond the browser, Cloudflare analyzes the TLS handshaking and HTTP/2 fingermark. Automated tools often get distinguishable TLS configuration that disagree from standard browsers. Mismatched cipher suites, extension orders, or sequences can expose automation even before your JavaScript executes.

Also Read:

Preparing Playwright for Evasion in 2026

Before diving into stealth plugins and advanced techniques, you demand to set up Playwright with the right foundation. A few configuration tweaks during initialization can significantly reduce catching rates and make your automation appear more legitimate.

1. Use Chromium with Proper Launch Arguments

Start by launching Playwright with arguments that disable automation flags and mimic a real browser environment. The nonremittal Playwright setup screams & # 8220; bot, & # 8221; so you & # 8217; ll need to reverse several Chrome flags:

const browser = await chromium.launch ({headless: mistaken, // Headless mode is easier to detect
args: [
& # 8216; & # 8211; disable-blink-features=AutomationControlled & # 8217;,
& # 8216; & # 8211; disable-dev-shm-usage & # 8217;,
& # 8216; & # 8211; no-sandbox & # 8217;,
& # 8216; & # 8211; disable-setuid-sandbox & # 8217;,
& # 8216; & # 8211; disable-web-security & # 8217;,
& # 8216; & # 8211; disable-features=IsolateOrigins, site-per-process & # 8217;
]
});

Setting headless: mistakenis all-important for testing surroundings where optic verification matters, though newer headless modes are hard to detect than older versions.

Also Read:

2. Remove Automation Indicators

After found the browser, inject JavaScript that removes common automation signatures. Cloudflare chit for properties like navigator.webdriver, so you & # 8217; ll postulate to overrule these before navigating to any Cloudflare-protected page:

const context = await browser.newContext (); await context.addInitScript (() = & gt; {
Object.defineProperty (navigator, & # 8216; webdriver & # 8217;, {
get: () = & gt; undefined
});

window.chrome = {
runtime: {}
};
});

This script runs before any page loads, control Cloudflare & # 8217; s JavaScript never see mechanization mark.

Also Read:

3. Set Realistic Browser Context

Configure your browser circumstance with naturalistic viewport sizes, user agents, and locale scene. Avoid using obvious mechanization user agent or strange:

const context = await browser.newContext ({viewport: {width: 1920, tiptop: 1080},
userAgent: & # 8216; Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 & # 8217;,
locale: & # 8216; en-US & # 8217;,
timezoneId: & # 8216; America/New_York & # 8217;,
permissions: [& # 8216; geolocation & # 8217;]
});

Consistency matters hither. If your exploiter agent claim you & # 8217; re on Windows but your canvas fingerprint hint macOS, Cloudflare will notice.

Also Read:

4. Enable JavaScript and Cookies

This sounds obvious, but ensure JavaScript is enabled and cooky are properly handled. Cloudflare swear heavily on JavaScript challenges and cookie-based tracking:

const circumstance = await browser.newContext ({javaScriptEnabled: true,
acceptDownloads: true,
ignoreHTTPSErrors: mistaken
});

With these foundational settings in place, you & # 8217; re ready to add stealing plugins and more sophisticated evasion techniques.

Read More:

Using Stealth Plugins and Fingerprint Spoofing

Even with proper launch contour, Playwright can still be discover through sophisticated fingerprinting proficiency. Stealth plugins and fingerprint randomisation add an extra layer of protection by mechanically handling detection vectors you might miss manually.

1. Install and Configure Playwright-Extra Stealth

The playwright-extra framework with the puppeteer-extra-plugin-stealth plugin is your best bet for comprehensive evasion. It patches wads of automation indicators mechanically:

const {chromium} = require (& # 8216; playwright-extra & # 8217;); const StealthPlugin = require (& # 8216; puppeteer-extra-plugin-stealth & # 8217;);

chromium.use (StealthPlugin ());

const browser = await chromium.launch ({
headless: mistaken
});

This plugin handles navigator.webdriver removal, chrome objective fixes, permissions API spoofing, and plugin array handling without manual intervention. It & # 8217; s actively maintained and updated to foresee new catching methods.

2. Randomize Canvas Fingerprints

Canvas fingerprinting is one of Cloudflare & # 8217; s most reliable detection methods. Each browser renders canvas elements slightly otherwise based on hardware and package configurations. Add dissonance to your canvas to avoid reproducible fingerprints:

await context.addInitScript (() = & gt; {const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
HTMLCanvasElement.prototype.toDataURL = office (type) {
const canvas = this;
const ctx = canvas.getContext (& # 8216; 2d & # 8217;);
const imageData = ctx.getImageData (0, 0, canvas.width, canvas.height);

// Add minimal noise
for (let i = 0; i & lt; imageData.data.length; i += 4) {
imageData.data [i] += Math.floor (Math.random () * 3) & # 8211; 1;
}

ctx.putImageData (imageData, 0, 0);
return originalToDataURL.call (this, type);
};
});

Read More:

3. Spoof WebGL Fingerprints

WebGL rendering cater another unique fingerprint. Randomize WebGL parameters to avoid detection:

await context.addInitScript (() = & gt; {const getParameter = WebGLRenderingContext.prototype.getParameter;
WebGLRenderingContext.prototype.getParameter = function (parameter) {
if (argument === 37445) {
return & # 8216; Intel Inc. & # 8217;; // UNMASKED_VENDOR_WEBGL
}
if (parameter === 37446) {
homecoming & # 8216; Intel Iris OpenGL Engine & # 8217;; // UNMASKED_RENDERER_WEBGL
}
return getParameter.call (this, argument);
};
});

4. Randomize Fonts and Plugins

Cloudflare checks available fonts and browser plugins. Use different font lists and plugin configurations across session:

await context.addInitScript (() = & gt; {Object.defineProperty (navigator, & # 8216; plugins & # 8217;, {
get: () = & gt; [
{
name: & # 8216; Chrome PDF Plugin & # 8217;,
filename: & # 8216; internal-pdf-viewer & # 8217;,
description: & # 8216; Portable Document Format & # 8217;
},
{
name: & # 8216; Chrome PDF Viewer & # 8217;,
filename: & # 8216; mhjfbmdgcfjbbpaeojofohoefgiehjai & # 8217;,
description: & # 8221;
}
]
});
});

5. Use FingerprintJS for Testing

Before hit Cloudflare-protected sites, test your fingermark consistence using FingerprintJS or similar services. This facilitate identify what & # 8217; s leak your automation:

const page = await context.newPage (); await page.goto (& # 8216; https: //fingerprint.com/demo/ & # 8217;);
await page.waitForTimeout (3000);
// Check if fingermark changes between runs

For autonomous testing across multiple user personas, check out SUSATest — it explores your app like 10 different real users.

With stealth plugins and fingerprint spoofing in place, your Playwright instance seem significantly more like a existent browser. Next, we & # 8217; ll tackle IP repute and geolocation issues.

Also Read:

Proxy Rotation, Residential IPs & amp; Geolocation in Playwright

Even with perfect browser fingerprinting, your IP address can afford you off. Cloudflare tracks IP reputation, request figure, and geolocation consistency. Using datacenter IPs or get too many requests from a single address will get you blocked fast.

Why Residential Proxies Matter

Datacenter IPs are flagged by Cloudflare because they & # 8217; re associated with host providers, not real users. Residential procurator route your traffic through real ISP-assigned IP addresses, make your requests appear legitimate. For testing surroundings that need to bypass Cloudflare, residential proxies are essential.

1. Configure Playwright with Proxy Support

Playwright supports, HTTPS, and SOCKS5 proxies. Configure them during context creation:

const context = await browser.newContext ({placeholder: {
waiter: & # 8216; http: //proxy-server.com:8080 & # 8217;,
username: & # 8216; your-username & # 8217;,
password: & # 8216; your-password & # 8217;
}
});

For authenticated proxies, include credentials instantly in the configuration. Most proxy providers offer rotating residential IPs that change with each request or session.

Also Read:

2. Implement Proxy Rotation

Don & # 8217; t use the same IP for every petition. Rotate proxies between exam runs or even between page navigations:

const proxies = [{waiter: & # 8216; http: //proxy1.com:8080 & # 8217;, username: & # 8216; user1 & # 8217;, password: & # 8216; pass1 & # 8217;},
{server: & # 8216; http: //proxy2.com:8080 & # 8217;, username: & # 8216; user2 & # 8217;, password: & # 8216; pass2 & # 8217;},
{host: & # 8216; http: //proxy3.com:8080 & # 8217;, username: & # 8216; user3 & # 8217;, password: & # 8216; pass3 & # 8217;}
];

function getRandomProxy () {
homecoming proxies [Math.floor (Math.random () * proxies.length)];
}

const setting = await browser.newContext ({
proxy: getRandomProxy ()
});

This prevents Cloudflare from associating multiple requests with a individual IP and triggering rate limits.

3. Match Geolocation with IP Address

If your proxy IP is in New York but your browser timezone says Los Angeles, Cloudflare will notice. Always match your geolocation settings with your proxy localisation:

const context = await browser.newContext ({proxy: {
server: & # 8216; http: //us-east-proxy.com:8080 & # 8217;
},
locus: & # 8216; en-US & # 8217;,
timezoneId: & # 8216; America/New_York & # 8217;,
geolocation: {latitude: 40.7128, longitude: -74.0060},
permissions: [& # 8216; geolocation & # 8217;]
});

Check your proxy provider & # 8217; s documentation for accurate geolocation coordinates for each IP.

Read More:

4. Use Mucilaginous Sessions When Needed

Some testing scenarios postulate maintaining the like IP across multiple requests (like testing a complete user journeying). Use steamy session proxies that keep the same IP for a defined period:

const context = await browser.newContext ({placeholder: {
server: & # 8216; http: //sticky-proxy.com:8080 & # 8217;,
username: & # 8216; user-session-123 & # 8217;, // Session identifier
password: & # 8216; your-password & # 8217;
}
});

However, running multiple Playwright book in parallel across different IPs, regions, and device requires infrastructure that is difficult to maintain locally. Tools like BrowserStack let teams scale essay effortlessly by providing instantaneous access to grand of real browser and devices in the cloud, eliminating proxy management overhead.

Simulating Human Activity and Navigation Patterns

Perfect browser configuration and residential IPs won & # 8217; t help if your Playwright script navigates like a automaton. Cloudflare analyzes behavioral patterns, and inhuman precision is a dead giveaway. You need to add haphazardness, hold, and realistic interaction to your mechanisation.

1. Add Random Delays Between Actions

Existent exploiter don & # 8217; t click button instantly or navigate at machine speeding. Introduce random delays between activeness:

function randomDelay (min, max) {homecoming Math.floor (Math.random () * (max & # 8211; min + 1) + min);
}

await page.click (& # 8216; # login-button & # 8217;);
await page.waitForTimeout (randomDelay (1000, 3000)); // Wait 1-3 seconds
await page.fill (& # 8216; # username & # 8217;, & # 8216; testuser & # 8217;);
await page.waitForTimeout (randomDelay (500, 1500));

Also Read:

2. Simulate Mouse Movements

Humans locomote their mouse around the page before clicking. Add realistic shiner motion:

async function humanClick (page, chooser) {const element = await page.locator (picker);
const box = await element.boundingBox ();

if (box) {
// Move to random position near the element firstly
await page.mouse.move (
box.x + Math.random () * box.width,
box.y + Math.random () * box.height,
{steps: randomDelay (5, 15)}
);

await page.waitForTimeout (randomDelay (100, 500));
await element.click ();
}
}

await humanClick (page, & # 8216; # submit-button & # 8217;);

3. Add Scroll Behavior

Users scroll through pages naturally. Add random scrolling before interacting with elements:

async function humanScroll (page) {const scrollHeight = await page.evaluate (() = & gt; document.body.scrollHeight);
const viewportHeight = await page.evaluate (() = & gt; window.innerHeight);

// Scroll in glob
for (let i = 0; i window.scrollTo (0, y), scrollTo);
await page.waitForTimeout (randomDelay (500, 1500));
}
}

await page.goto (& # 8216; https: //example.com & # 8217;);
await humanScroll (page);
await page.click (& # 8216; # target-element & # 8217;);

Read More:

4. Type Like a Human

Don & # 8217; t fill form battlefield instantly. Use type () with delays instead of fill ():

async function humanType (page, selector, text) {await page.click (chooser);

for (const woman of textbook) {
await page.keyboard.type (charr);
await page.waitForTimeout (randomDelay (50, 150)); // 50-150ms per character
}
}

await humanType (page, & # 8216; # netmail & # 8217;, & # 8216; user @ example.com & # 8217;);

Add episodic typos and rectification for even more reality:

async function humanTypeWithErrors (page, selector, text) {await page.click (selector);

for (let i = 0; i & lt; text.length; i++) {
// 5 % hazard of typo
if (Math.random () 0) {
await page.keyboard.type (& # 8216; x & # 8217;); // Incorrect character
await page.waitForTimeout (randomDelay (100, 300));
await page.keyboard.press (& # 8216; Backspace & # 8217;);
await page.waitForTimeout (randomDelay (50, 150));
}

await page.keyboard.type (text [i]);
await page.waitForTimeout (randomDelay (50, 150));
}
}

5. Mimic Reading Time

Users spend time reading content before acting. Add delays relative to content length:

async function simulateReading (page, selector) {const text = await page.textContent (selector);
const wordCount = text.split (& # 8216; & # 8216;) .length;
const readingTime = (wordCount / 200) * 60 * 1000; // 200 words per minute

await page.waitForTimeout (randomDelay (readingTime * 0.5, readingTime * 1.5));
}

await page.goto (& # 8216; https: //example.com/article & # 8217;);
await simulateReading (page, & # 8216; article & # 8217;);
await page.click (& # 8216; # next-page & # 8217;);

Handling Cloudflare Turnstile, CAPTCHA and Advanced Challenges

Even with perfect configuration, you & # 8217; ll occasionally encounter Cloudflare & # 8217; s challenge page. Turnstile, CAPTCHAs, and JavaScript challenges expect specific manage strategy to keep your automation running smoothly.

Cloudflare apply several challenge mechanisms:

  • Turnstile: A CAPTCHA alternative that control browser authenticity without user interaction

Read More:

  • Managed Challenge: JavaScript-based confirmation that happens automatically
  • Interactive Challenge: Traditional CAPTCHA requiring manual resolution
  • Block Page: Difficult block with no bypass option

Your access calculate on which challenge you encounter.

Detecting Challenge Pages

Before handling challenge, detect when you & # 8217; ve hit one:

async function isCloudflareChallenge (page) {const title = await page.title ();
const content = await page.content ();

homecoming title.includes (& # 8216; Just a bit & # 8217;) ||
content.includes (& # 8216; Checking your browser & # 8217;) ||
content.includes (& # 8216; cloudflare & # 8217;) ||
await page.locator (& # 8216; # challenge-form & # 8217;) .isVisible () .catch (() = & gt; mistaken);
}

await page.goto (& # 8216; https: //example.com & # 8217;);

if (await isCloudflareChallenge (page)) {
console.log (& # 8216; Cloudflare challenge observe & # 8217;);
// Handle accordingly
}

Wait for Automatic Challenges to Complete

Many Cloudflare challenge resolve automatically if your configuration is right. Simply wait:

async function waitForCloudflareChallenge (page, timeout = 30000) {if (await isCloudflareChallenge (page)) {
console.log (& # 8216; Waiting for Cloudflare challenge to conclude & # 8230; & # 8217;);
await page.waitForFunction (
() = & gt;! document.title.includes (& # 8216; Just a moment & # 8217;),
{timeout}
);
await page.waitForTimeout (randomDelay (2000, 4000)); // Extra guard delay
}
}

await page.goto (& # 8216; https: //example.com & # 8217;);
await waitForCloudflareChallenge (page);

Handle Turnstile Challenges

Turnstile challenges often pass automatically with proper stealth contour. If not, you may take to expect longer:

async function handleTurnstile (page) {const turnstileFrame = page.frameLocator (& # 8216; iframe [src * = & # 8221; challenges.cloudflare.com & # 8221;] & # 8217;);

try {
await turnstileFrame.locator (& # 8216; # challenge-stage & # 8217;) .waitFor ({
state: & # 8216; hidden & # 8217;,
timeout: 30000
});

console.log (& # 8216; Turnstile challenge passed & # 8217;);
await page.waitForTimeout (randomDelay (1000, 3000));
} catch (error) {
console.log (& # 8216; Turnstile challenge may require manual intervention & # 8217;);
}
}

Integrate CAPTCHA Solving Services

For interactive CAPTCHAs, integrate third-party solving services like 2Captcha, Anti-Captcha, or CapSolver:

async mapping solveCaptcha (page, apiKey) {const siteKey = await page.getAttribute (& # 8216; [data-sitekey] & # 8217;, & # 8216; data-sitekey & # 8217;);
const pageUrl = page.url ();

// Send to solving service
const reaction = await fetch (& # 8216; https: //2captcha.com/in.php & # 8217;, {
method: & # 8216; POST & # 8217;,
body: JSON.stringify ({
key: apiKey,
method: & # 8216; turnstile & # 8217;,
sitekey: siteKey,
pageurl: pageUrl
})
});

const taskId = await response.json ();

// Poll for solution
let solvent;
for (let i = 0; i setTimeout (resolve, 5000));

const consequence = await fetch (` https: //2captcha.com/res.php? key= $ {apiKey} & amp; action=get & amp; id= $ {taskId} `);
const data = await result.text ();

if (data.includes (& # 8216; OK| & # 8217;)) {
solution = data.split (& # 8216; | & # 8217;) [1];
break;
}
}

// Inject solution
if (solution) {
await page.evaluate ((token) = & gt; {
document.querySelector (& # 8216; [name= & # 8221; cf-turnstile-response & # 8221;] & # 8217;) .value = token;
}, solvent);

await page.click (& # 8216; # challenge-form button [type= & # 8221; submit & # 8221;] & # 8217;);
}
}

Retry Logic for Failed Challenges

Implement retry mechanism when challenge fail:

async role navigateWithRetry (page, url, maxRetries = 3) {for (let seek = 1; attempt

Testing and Validating the Bypass Flow on BrowserStack

Bypass techniques can successfully overcome Cloudflare challenges in a controlled environment. The next pace is confirming that these solutions remain true when automation runs across different browsers, operating system, IP regions, and twist capabilities.

Cloudflare applies chance scoring dynamically so behavior that appears legitimate in one circumstance can still be flagged in others.

Testing on ensures automated flow continue to work under real-world weather. It gives you accession to over 3,500+ browser and device combination. You can browse through the logs, screenshots, and picture playback to control where Cloudflare espial tightens and refine fingerprinting or proxy configurations before deployment.

Here are some key characteristic of for testing bypass flow with Playwright.

  • : Test Cloudflare bypass flows on physical devices and existent browser to ensure production-level behavior.
  • Global Infrastructure: Validate proxy and geolocation scope with test runs from multiple area that match real residential routing.
  • : Compare multiple shunt strategy at once across different browser and OS combinations.
  • Session Insights: Use captured video, log, and network traces to nail where Cloudflare challenges trigger.
  • Instant Browser Access: Spin up any browser version quickly to verify compatibility without local setup.

Talk to an Expert

Conclusion

Cloudflare & # 8217; s bot protection introduces complex detection signals that challenge traditional browser automation workflows. Leveraging Playwright with the right configuration, fingerprinting safeguards, and proxy scheme can aid automation behave more like a existent user and successfully navigate Cloudflare & # 8217; s defense systems.

To corroborate that these bypass strategies hold in real usage conditions, BrowserStack ply the final layer of confidence. Real device coverage, global routing options, and detailed debugging insights allow team to substantiate that their Playwright automations rest consistent across environment that Cloudflare evaluates differently.

Useful Resources for Playwright

Tool Comparisons:

Tags
7,000+ Views

# Ask-and-Contributeabout this topic with our Discord community.

Related Guides

Automate This With SUSA

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts needed.

Try SUSA Free

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free