How to Bypass Cloudflare with Playwright in 2026
On This Page How Cloudflare Detects Automation with PlaywrightApril 13, 2026 · 17 min read · Tool Comparison
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? 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: 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: Once set up, this enables features such as: 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. 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. 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: 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. 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. 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: 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: 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. 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: 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: 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: window.chrome = { This script runs before any page loads, control Cloudflare & # 8217; s JavaScript never see mechanization mark. Also Read: Configure your browser circumstance with naturalistic viewport sizes, user agents, and locale scene. Avoid using obvious mechanization user agent or strange: Consistency matters hither. If your exploiter agent claim you & # 8217; re on Windows but your canvas fingerprint hint macOS, Cloudflare will notice. Also Read: This sounds obvious, but ensure JavaScript is enabled and cooky are properly handled. Cloudflare swear heavily on JavaScript challenges and cookie-based tracking: With these foundational settings in place, you & # 8217; re ready to add stealing plugins and more sophisticated evasion techniques. Read More: 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: chromium.use (StealthPlugin ()); const browser = await chromium.launch ({ 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: // Add minimal noise ctx.putImageData (imageData, 0, 0); Read More: 3. Spoof WebGL Fingerprints WebGL rendering cater another unique fingerprint. Randomize WebGL parameters to avoid detection: 4. Randomize Fonts and Plugins Cloudflare checks available fonts and browser plugins. Use different font lists and plugin configurations across session: 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: 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: 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: 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: function getRandomProxy () { const setting = await browser.newContext ({ 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: 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: 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. 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: await page.click (& # 8216; # login-button & # 8217;); Also Read: 2. Simulate Mouse Movements Humans locomote their mouse around the page before clicking. Add realistic shiner motion: if (box) { await page.waitForTimeout (randomDelay (100, 500)); await humanClick (page, & # 8216; # submit-button & # 8217;); 3. Add Scroll Behavior Users scroll through pages naturally. Add random scrolling before interacting with elements: // Scroll in glob await page.goto (& # 8216; https: //example.com & # 8217;); Read More: 4. Type Like a Human Don & # 8217; t fill form battlefield instantly. Use type () with delays instead of fill (): for (const woman of textbook) { await humanType (page, & # 8216; # netmail & # 8217;, & # 8216; user @ example.com & # 8217;); Add episodic typos and rectification for even more reality: for (let i = 0; i & lt; text.length; i++) { await page.keyboard.type (text [i]); 5. Mimic Reading Time Users spend time reading content before acting. Add delays relative to content length: await page.waitForTimeout (randomDelay (readingTime * 0.5, readingTime * 1.5)); await page.goto (& # 8216; https: //example.com/article & # 8217;); 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: Read More: Your access calculate on which challenge you encounter. Detecting Challenge Pages Before handling challenge, detect when you & # 8217; ve hit one: homecoming title.includes (& # 8216; Just a bit & # 8217;) || await page.goto (& # 8216; https: //example.com & # 8217;); if (await isCloudflareChallenge (page)) { Wait for Automatic Challenges to Complete Many Cloudflare challenge resolve automatically if your configuration is right. Simply wait: await page.goto (& # 8216; https: //example.com & # 8217;); Handle Turnstile Challenges Turnstile challenges often pass automatically with proper stealth contour. If not, you may take to expect longer: try { console.log (& # 8216; Turnstile challenge passed & # 8217;); Integrate CAPTCHA Solving Services For interactive CAPTCHAs, integrate third-party solving services like 2Captcha, Anti-Captcha, or CapSolver: // Send to solving service const taskId = await response.json (); // Poll for solution const consequence = await fetch (` https: //2captcha.com/res.php? key= $ {apiKey} & amp; action=get & amp; id= $ {taskId} `); if (data.includes (& # 8216; OK| & # 8217;)) { // Inject solution await page.click (& # 8216; # challenge-form button [type= & # 8221; submit & # 8221;] & # 8217;); Retry Logic for Failed Challenges Implement retry mechanism when challenge fail: 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. 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. Tool Comparisons: On This Page # Ask-and-Contributeabout this topic with our Discord community. Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts needed. Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.How to Bypass Cloudflare with Playwright in 2026
Overview
npm install -s @ cloudflare/playwright
compatibility_date = & # 8220; 2025-09-15 & # 8221;
compatibility_flags = [& # 8220; nodejs_compat & # 8221;]
browser = {binding = & # 8220; MYBROWSER & # 8221;}How Cloudflare Detects Automation with Playwright
1. Browser Fingerprinting
2. Automation Indicators
3. Behavioral Analysis
4. Network and IP Reputation
5. TLS and HTTP Fingerprinting
Preparing Playwright for Evasion in 2026
1. Use Chromium with Proper Launch Arguments
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;
]
});2. Remove Automation Indicators
const context = await browser.newContext (); await context.addInitScript (() = & gt; {
Object.defineProperty (navigator, & # 8216; webdriver & # 8217;, {
get: () = & gt; undefined
});
runtime: {}
};
});3. Set Realistic Browser Context
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;]
});4. Enable JavaScript and Cookies
const circumstance = await browser.newContext ({javaScriptEnabled: true,
acceptDownloads: true,
ignoreHTTPSErrors: mistaken
});Using Stealth Plugins and Fingerprint Spoofing
const {chromium} = require (& # 8216; playwright-extra & # 8217;); const StealthPlugin = require (& # 8216; puppeteer-extra-plugin-stealth & # 8217;);
headless: mistaken
});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);
for (let i = 0; i & lt; imageData.data.length; i += 4) {
imageData.data [i] += Math.floor (Math.random () * 3) & # 8211; 1;
}
return originalToDataURL.call (this, type);
};
});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);
};
});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;
}
]
});
});const page = await context.newPage (); await page.goto (& # 8216; https: //fingerprint.com/demo/ & # 8217;);
await page.waitForTimeout (3000);
// Check if fingermark changes between runsProxy Rotation, Residential IPs & amp; Geolocation in Playwright
const context = await browser.newContext ({placeholder: {
waiter: & # 8216; http: //proxy-server.com:8080 & # 8217;,
username: & # 8216; your-username & # 8217;,
password: & # 8216; your-password & # 8217;
}
});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;}
];
homecoming proxies [Math.floor (Math.random () * proxies.length)];
}
proxy: getRandomProxy ()
});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;]
});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;
}
});Simulating Human Activity and Navigation Patterns
function randomDelay (min, max) {homecoming Math.floor (Math.random () * (max & # 8211; min + 1) + min);
}
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));async function humanClick (page, chooser) {const element = await page.locator (picker);
const box = await element.boundingBox ();
// 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 element.click ();
}
}async function humanScroll (page) {const scrollHeight = await page.evaluate (() = & gt; document.body.scrollHeight);
const viewportHeight = await page.evaluate (() = & gt; window.innerHeight);
for (let i = 0; i window.scrollTo (0, y), scrollTo);
await page.waitForTimeout (randomDelay (500, 1500));
}
}
await humanScroll (page);
await page.click (& # 8216; # target-element & # 8217;);async function humanType (page, selector, text) {await page.click (chooser);
await page.keyboard.type (charr);
await page.waitForTimeout (randomDelay (50, 150)); // 50-150ms per character
}
}async function humanTypeWithErrors (page, selector, text) {await page.click (selector);
// 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.waitForTimeout (randomDelay (50, 150));
}
}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 simulateReading (page, & # 8216; article & # 8217;);
await page.click (& # 8216; # next-page & # 8217;);Handling Cloudflare Turnstile, CAPTCHA and Advanced Challenges
async function isCloudflareChallenge (page) {const title = await page.title ();
const content = await page.content ();
content.includes (& # 8216; Checking your browser & # 8217;) ||
content.includes (& # 8216; cloudflare & # 8217;) ||
await page.locator (& # 8216; # challenge-form & # 8217;) .isVisible () .catch (() = & gt; mistaken);
}
console.log (& # 8216; Cloudflare challenge observe & # 8217;);
// Handle accordingly
}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 waitForCloudflareChallenge (page);async function handleTurnstile (page) {const turnstileFrame = page.frameLocator (& # 8216; iframe [src * = & # 8221; challenges.cloudflare.com & # 8221;] & # 8217;);
await turnstileFrame.locator (& # 8216; # challenge-stage & # 8217;) .waitFor ({
state: & # 8216; hidden & # 8217;,
timeout: 30000
});
await page.waitForTimeout (randomDelay (1000, 3000));
} catch (error) {
console.log (& # 8216; Turnstile challenge may require manual intervention & # 8217;);
}
}async mapping solveCaptcha (page, apiKey) {const siteKey = await page.getAttribute (& # 8216; [data-sitekey] & # 8217;, & # 8216; data-sitekey & # 8217;);
const pageUrl = page.url ();
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
})
});
let solvent;
for (let i = 0; i setTimeout (resolve, 5000));
const data = await result.text ();
solution = data.split (& # 8216; | & # 8217;) [1];
break;
}
}
if (solution) {
await page.evaluate ((token) = & gt; {
document.querySelector (& # 8216; [name= & # 8221; cf-turnstile-response & # 8221;] & # 8217;) .value = token;
}, solvent);
}
}async role navigateWithRetry (page, url, maxRetries = 3) {for (let seek = 1; attemptTesting and Validating the Bypass Flow on BrowserStack
Conclusion
Useful Resources for Playwright
Related Guides
Automate This With SUSA
Test Your App Autonomously