Point headless Chrome at a Cloudflare protected site and you rarely get a screenshot. You get an HTTP 403, or a "Checking your browser" interstitial frozen in the middle of your capture.
By the end of this post you will know why Cloudflare blocks your capture, what the do-it-yourself fix actually costs, and the single HTTP call that returns the real rendered page.
The short version: vanilla headless Chromium looks like a bot to Cloudflare, so it gets challenged before the page ever renders. You can fight that with a stealth plugin, a residential proxy, and a standing maintenance budget, or you can send one GET request to ScreenshotRender with Stealth Mode and skip the fight. A stealth plugin on its own is not enough either, and the second section shows exactly why.
Why does headless Chrome get blocked by Cloudflare?
Headless Chrome gets blocked because Cloudflare's bot management fingerprints it as automation before the page loads, then serves a challenge instead of the real content. The check happens at the edge, so your screenshot code never even reaches a rendered DOM.
Four signals do most of the work. navigator.webdriver is set to true in any browser driven by Puppeteer or Playwright, and that single property is a giveaway. The navigator.plugins array is empty in a stock headless build, where a real desktop Chrome reports several entries. The TLS handshake has its own fingerprint, and the one produced by most automation HTTP stacks matches a known library rather than a consumer browser. On top of that, the default launch flags Puppeteer and Playwright pass to Chromium are themselves a recognizable pattern.
Cloudflare's Bot Management documentation describes how these signals feed a bot score, and the Turnstile challenge is what a low score earns you. The page you wanted is behind that interstitial, and a screenshot taken now captures the challenge, not the content.
Can you screenshot a Cloudflare site with Puppeteer or Playwright?
You can, but not with a stock install. Both libraries take a screenshot with one method call; the hard part is getting past the challenge so there is a real page to capture.
The capture itself is trivial. Navigate to the URL, then call page.screenshot({ fullPage: true }) and you have a PNG in memory. The Puppeteer Page.screenshot reference documents the whole API surface. None of that matters if Cloudflare returned a 403, because the page in memory is the challenge.
Getting a real page takes three moving parts that all need upkeep:
- A stealth plugin to patch the obvious fingerprints, so
navigator.webdriverreportsfalseand the plugin array looks populated. - A residential proxy so the request does not arrive from a datacenter IP range that Cloudflare has already flagged. Datacenter ranges score badly on their own.
- Rotating user agents and headers that stay consistent with the proxy region, because a US residential IP paired with a stale mobile user agent is its own tell.
Each part works until Cloudflare ships a new detection rule, and then you are patching again. There is also a flat infrastructure cost underneath all of it: a warm Chromium is memory hungry, so a cheap server handles only a handful of parallel screenshots before it runs out of RAM. Most teams spend months on this before they decide the screenshot was never the part worth owning.
Stop fighting Cloudflare. Just get the screenshot.
Skip the stealth plugin, the residential proxy budget, and the EC2 fleet. One HTTP GET to ScreenshotRender with Stealth Mode renders the real page and returns a clean PNG.
Try a renderHow do you screenshot a Cloudflare protected website?
Send one HTTP GET to ScreenshotRender with Stealth Mode on your account and you get back a hosted PNG of the real page, no challenge and no proxy to manage. The entire request is one line you can copy:
https://screenshotrender.com/api/v1/screenshot?apiKey=YOUR_API_KEY&url=https://www.g2.com&fullPage=true
Three parameters do the work. apiKey is your sr- prefixed key from the ScreenshotRender dashboard. url is the Cloudflare protected page you want to capture, percent encoded if it carries its own query string. fullPage=true captures the entire scrollable document, the same option that drives a full page screenshot on any other site. The response is JSON with the hosted image at data.screenshot.
Notice what is not in that URL: no proxy setting, no fingerprint flag, no stealth parameter. Stealth Mode is a plan level feature, so it applies automatically to every request once your account is on a plan that includes it. Under the hood ScreenshotRender renders with a real Chromium browser, which is what makes the captured page match what a human visitor sees. Cookie consent banners, GDPR popups, ad overlays, and chat widgets are stripped before the capture on every plan, so the Cloudflare site you screenshot comes back clean as well as unblocked.
Test the exact call against any Cloudflare protected site in the interactive playground before you wire it into code, so you can see the render succeed before a workflow depends on it.
When does screenshotting a Cloudflare site still fail?
Three situations where Stealth Mode needs a workaround or will not help, because no honest screenshot guide claims a 100 percent hit rate:
- The free tier does not include Stealth Mode. ScreenshotRender's free plan removes cookie banners and ads and captures full pages, but Stealth Mode starts on the Hobby plan. A free key pointed at a Cloudflare protected site will capture the challenge, not the content. See the pricing page for the plan breakdown.
- Pages behind a login. The public API takes a URL, not a session cookie. If the Cloudflare protected page also requires authentication, generate a signed share URL on the source app first and screenshot that, or host your own browser inside the authenticated session.
- The hardest interactive challenges. Most protected pages render without a challenge appearing at all, but an aggressive interactive CAPTCHA can still stop a render. Treat the result as a high success rate, not a guarantee, and pass a
timeoutvalue so a blocked request fails fast instead of hanging your job.
For the common case, a public marketing page, a competitor's pricing page, a product listing, or a review site sitting behind Cloudflare, one GET request is the whole solution.
Common questions about screenshotting Cloudflare protected sites
Why does headless Chrome get a 403 from Cloudflare?
Cloudflare Bot Management inspects each request for automation signals before serving the page: the navigator.webdriver flag set to true, an empty plugins array, a TLS fingerprint that matches a known automation library, and the default launch flags Puppeteer and Playwright pass to Chromium. When enough signals line up, Cloudflare returns a 403 or a JavaScript challenge page instead of the real content. A normal browser on a real machine passes all of those checks, which is why the same page loads fine for you by hand.
Can you screenshot a website behind Cloudflare with Puppeteer?
Yes, but not with a stock Puppeteer install. You need a stealth plugin to patch the obvious automation fingerprints, a residential proxy so the request does not come from a flagged datacenter IP range, and rotating user agents. You also have to update all three every time Cloudflare ships a new detection rule. The screenshot itself is one page.screenshot call; getting a real page to screenshot is the work.
Is there a screenshot API that handles Cloudflare?
Yes. ScreenshotRender has a Stealth Mode that handles Cloudflare challenges, bot detection, and similar anti-scraping measures so a single GET request returns the rendered page. It is bundled on the Hobby plan and above. You pass the same url and fullPage parameters as any other request; there is no separate proxy or fingerprint configuration to manage.
Does ScreenshotRender bypass Cloudflare Turnstile and CAPTCHAs?
Stealth Mode is built to get past the bot-detection layer that triggers Cloudflare's challenges in the first place, including Turnstile interstitials, so most protected pages render without a challenge ever appearing. No screenshot service clears every interactive CAPTCHA every time, so treat it as a high success rate rather than a guarantee, and pass a timeout value so a blocked request fails fast instead of hanging.
Is there a free way to screenshot a Cloudflare protected site?
ScreenshotRender's free plan includes 100 screenshots per month with no credit card, but Stealth Mode is not part of it, so the free tier handles ordinary pages rather than Cloudflare protected ones. Stealth Mode starts on the Hobby plan, which is $10 per month billed annually and includes 2,000 screenshots. Cookie banner and ad removal are enabled on every plan including the free one.
The honest verdict: screenshotting a Cloudflare protected site with raw Puppeteer is possible, but the stealth plugin, the proxy budget, and the rule-chasing maintenance rarely pay for themselves. A hosted call with Stealth Mode turns the whole problem into one URL, and the free tier is enough to confirm the ordinary pages render before you upgrade for the protected ones.



