Get started
Cloudflare Browser Rendering allows you to programmatically control a headless browser, enabling you to do things like take screenshots, generate PDFs, and perform automated browser tasks.
There are two ways to use Browser Rendering:
- REST API for simple, one-off actions, like taking a screenshot, fetching HTML, or generating a PDF.
- Workers Bindings for more complex, multi-step browser automation using Puppeteer and Playwright.
This guide will help you choose the right path for your needs and get you started with your first Browser Rendering project.
The REST API is best for situations where you have an existing application and want to perform simple, stateless browser actions.
- Sign up for a Cloudflare account ↗.
- Create a Cloudflare API Token with
Browser Rendering - Edit
permissions.
curl -X POST 'https://api.cloudflare.com/client/v4/accounts/<accountId>/browser-rendering/screenshot' \ -H 'Authorization: Bearer <apiToken>' \ -H 'Content-Type: application/json' \ -d '{ "html": "Hello World!", "screenshotOptions": { "omitBackground": true } }' \ --output "screenshot.png"
The REST API can also be used to:
Workers Bindings are best for situations where you need to build more complex, multi-step browser automation workflows. You can use familiar tools like Puppeteer and Playwright.
- Sign up for a Cloudflare account ↗.
- Install
Node.js
↗.
Node.js version manager
Use a Node version manager like Volta ↗ or nvm ↗ to avoid permission issues and change Node.js versions. Wrangler, discussed later in this guide, requires a Node version of 16.17.0
or later.
Cloudflare Workers provides a serverless execution environment that allows you to create new applications or augment existing ones without configuring or maintaining infrastructure. Your Worker application is a container to interact with a headless browser to do actions, such as taking screenshots.
Create a new Worker project named browser-worker
by running:
npm create cloudflare@latest -- browser-worker
yarn create cloudflare browser-worker
pnpm create cloudflare@latest browser-worker
For setup, select the following options:
- For What would you like to start with?, choose
Hello World example
. - For Which template would you like to use?, choose
Worker only
. - For Which language do you want to use?, choose
JavaScript / TypeScript
. - For Do you want to use git for version control?, choose
Yes
. - For Do you want to deploy your application?, choose
No
(we will be making some changes before deploying).
In your browser-worker
directory, install Cloudflare’s fork of Puppeteer:
npm i -D @cloudflare/puppeteer
yarn add -D @cloudflare/puppeteer
pnpm add -D @cloudflare/puppeteer
Browser Rendering can be used with other developer products. You might need a relational database, an R2 bucket to archive your crawled pages and assets, a Durable Object to keep your browser instance alive and share it with multiple requests, or Queues to handle your jobs asynchronously.
For the purpose of this example, we will use a KV store to cache your screenshots.
Create two namespaces, one for production and one for development.
npx wrangler kv namespace create BROWSER_KV_DEMOnpx wrangler kv namespace create BROWSER_KV_DEMO --preview
Take note of the IDs for the next step.
Configure your browser-worker
project's Wrangler configuration file by adding a browser binding and a Node.js compatibility flag. Bindings allow your Workers to interact with resources on the Cloudflare developer platform. Your browser binding
name is set by you, this guide uses the name MYBROWSER
. Browser bindings allow for communication between a Worker and a headless browser which allows you to do actions such as taking a screenshot, generating a PDF, and more.
Update your Wrangler configuration file with the Browser Rendering API binding and the KV namespaces you created:
{ "name": "browser-worker", "main": "src/index.js", "compatibility_date": "2023-03-14", "compatibility_flags": [ "nodejs_compat" ], "browser": { "binding": "MYBROWSER" }, "kv_namespaces": [ { "binding": "BROWSER_KV_DEMO", "id": "22cf855786094a88a6906f8edac425cd", "preview_id": "e1f8b68b68d24381b57071445f96e623" } ]}
name = "browser-worker"main = "src/index.js"compatibility_date = "2023-03-14"compatibility_flags = [ "nodejs_compat" ]
browser = { binding = "MYBROWSER" }kv_namespaces = [ { binding = "BROWSER_KV_DEMO", id = "22cf855786094a88a6906f8edac425cd", preview_id = "e1f8b68b68d24381b57071445f96e623" }]
Update src/index.js
with your Worker code:
import puppeteer from "@cloudflare/puppeteer";
export default { async fetch(request, env) { const { searchParams } = new URL(request.url); let url = searchParams.get("url"); let img; if (url) { url = new URL(url).toString(); // normalize img = await env.BROWSER_KV_DEMO.get(url, { type: "arrayBuffer" }); if (img === null) { const browser = await puppeteer.launch(env.MYBROWSER); const page = await browser.newPage(); await page.goto(url); img = await page.screenshot(); await env.BROWSER_KV_DEMO.put(url, img, { expirationTtl: 60 * 60 * 24, }); await browser.close(); } return new Response(img, { headers: { "content-type": "image/jpeg", }, }); } else { return new Response("Please add an ?url=https://example.com/ parameter"); } },};
Update src/index.ts
with your Worker code:
import puppeteer from "@cloudflare/puppeteer";
interface Env { MYBROWSER: Fetcher; BROWSER_KV_DEMO: KVNamespace;}
export default { async fetch(request, env): Promise<Response> { const { searchParams } = new URL(request.url); let url = searchParams.get("url"); let img: Buffer; if (url) { url = new URL(url).toString(); // normalize img = await env.BROWSER_KV_DEMO.get(url, { type: "arrayBuffer" }); if (img === null) { const browser = await puppeteer.launch(env.MYBROWSER); const page = await browser.newPage(); await page.goto(url); img = (await page.screenshot()) as Buffer; await env.BROWSER_KV_DEMO.put(url, img, { expirationTtl: 60 * 60 * 24, }); await browser.close(); } return new Response(img, { headers: { "content-type": "image/jpeg", }, }); } else { return new Response("Please add an ?url=https://example.com/ parameter"); } },} satisfies ExportedHandler<Env>;
This Worker instantiates a browser using Puppeteer, opens a new page, navigates to the location of the 'url' parameter, takes a screenshot of the page, stores the screenshot in KV, closes the browser, and responds with the JPEG image of the screenshot.
If your Worker is running in production, it will store the screenshot to the production KV namespace. If you are running wrangler dev
, it will store the screenshot to the dev KV namespace.
If the same url
is requested again, it will use the cached version in KV instead, unless it expired.
Run npx wrangler dev
to test your Worker locally or run npx wrangler dev --remote
to test your Worker remotely before deploying to Cloudflare's global network.
To test taking your first screenshot, go to the following URL:
<LOCAL_HOST_URL>/?url=https://example.com
Run npx wrangler deploy
to deploy your Worker to the Cloudflare global network.
To take your first screenshot, go to the following URL:
<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/?url=https://example.com
If you have any feature requests or notice any bugs, share your feedback directly with the Cloudflare team by joining the Cloudflare Developers community on Discord ↗.
- Check out all the REST API endpoints
- Try out the Playwright MCP
- Learn more about Browser Rendering limits and pricing.
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark