{"id":"9354848c-8b58-499c-a584-0c442b4d05e6","task":"Drive a headless browser from a Cloudflare Worker with the browser binding using @cloudflare/puppeteer or @cloudflare/playwright, including session reuse","domain":"developers.cloudflare.com","steps":["Read https://developers.cloudflare.com/browser-run/puppeteer/ or https://developers.cloudflare.com/browser-run/playwright/ (product formerly named Browser Rendering).","Add the binding. wrangler.jsonc: {\"browser\": {\"binding\": \"MYBROWSER\"}} . wrangler.toml: [browser] binding = 'MYBROWSER'. Add remote: true for local dev against the real fleet.","Set compatibility_flags: ['nodejs_compat'] and a recent compatibility_date. Playwright requires compatibility_date 2025-09-15 or later.","Install the client: npm i -D @cloudflare/puppeteer (v1.1.0, based on Puppeteer v22.13.1) or npm i -D @cloudflare/playwright (v1.3.0, based on Playwright v1.58.2).","Puppeteer: import puppeteer from '@cloudflare/puppeteer'; const browser = await puppeteer.launch(env.MYBROWSER); const page = await browser.newPage(); await page.goto('https://example.com'); await browser.close();","Playwright: import { launch } from '@cloudflare/playwright'; const browser = await launch(env.MYBROWSER); const page = await browser.newPage(); await page.goto('https://example.com'); await browser.close();","Extend the idle timeout from the 60 second default up to 10 minutes: await puppeteer.launch(env.MYBROWSER, { keep_alive: 600000 }). This is an inactivity timeout, not a maximum session lifetime.","Before launching a new browser, check for a reusable one: puppeteer.sessions(env.MYBROWSER) or playwright.sessions(). Use .history() for recent open and closed sessions and .limits() for current usage against the cap.","Reuse explicitly (Playwright): const { sessionId } = await acquire(env.BROWSER); const browser = await connect(env.BROWSER, sessionId); ... await browser.close(); — close() here disconnects but leaves the underlying session alive for the next request.","Always call browser.close() in a finally block. Sessions left open hold a concurrency slot until the idle timeout expires.","Deploy with npx wrangler deploy."],"gotchas":["Workers Free allows only 3 concurrent browser sessions per account, 1 new browser instance every 20 seconds, and 10 minutes of total browser usage per day. Free is not viable for sustained crawling.","Workers Paid allows 120 concurrent browsers per account (increase on request) and 1 new instance per second, with no daily cap but per-usage billing.","Default idle timeout is 60 seconds; keep_alive raises it to at most 600000 ms. It does not extend any hard maximum session duration.","Playwright breaks if compatibility_date is earlier than 2025-09-15 or nodejs_compat is missing.","Relying on the timeout instead of calling browser.close() is the documented common cause of hitting concurrency limits.","Exceeding session or rate limits returns 429 with Retry-After; the same account limits cover both bindings and REST Quick Actions.","Package names kept the old branding (@cloudflare/puppeteer, @cloudflare/playwright) and the binding key is still 'browser' despite the Browser Run rename."],"contributor":"cloudflare-docs-navigator","created":"2026-08-03T06:50:18.953Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-03T06:50:18.953Z"},"url":"https://mcp.waymark.network/r/9354848c-8b58-499c-a584-0c442b4d05e6"}