Skip to content
AI / Browser Run

AI

Browser Run

Run @cloudflare/puppeteer against a hosted browser such as Browserbase, or any Chrome DevTools Protocol endpoint.

Status: Supported. Needs a service: set ORANGE_BROWSER_CDP_URL to a Chrome DevTools Protocol endpoint.

OpenOrange connects the browser binding to a browser that you name. The default path is a hosted browser, such as Browserbase. Any Chrome DevTools Protocol (CDP) endpoint works, including a Chromium that you run yourself.

Choose a provider

Set ORANGE_BROWSER_CDP_URL to the provider’s CDP WebSocket URL:

ProviderORANGE_BROWSER_CDP_URLNotes
Browserbasewss://connect.browserbase.com?apiKey=<API key>Each launch() starts a new browser. See Browserbase.
Browserlesswss://production-sfo.browserless.io?token=<API token>Other regions: production-lon and production-ams. A private fleet uses its own host, such as chrome.browserless.io.
Steelwss://connect.steel.dev?apiKey=<API key>Without sessionId, Steel starts a new session for each connection.
Other hosted CDP serviceIts CDP WebSocket URLThe URL must accept a new connection each time. See the note below.
Chromium that you runhttp://127.0.0.1:9222See Run Chromium yourself.

Each launch() opens a new connection to the same URL. A service that gives a WebSocket URL only after you create a session with its API, such as Hyperbrowser, does not fit one fixed URL.

Configuration

  1. Use the standard Wrangler binding:

    {
      "compatibility_flags": ["nodejs_compat"],
      "browser": { "binding": "BROWSER" }
    }
  2. Set ORANGE_BROWSER_CDP_URL to the CDP endpoint. For Browserbase, the endpoint carries the API key:

    # .dev.vars
    ORANGE_BROWSER_CDP_URL="wss://connect.browserbase.com?apiKey=<your API key>"
  3. Run orange dev.

Value of ORANGE_BROWSER_CDP_URLWhat OpenOrange does
A ws:// or wss:// address, such as a hosted browser’s endpoint or ws://127.0.0.1:9222/devtools/browser/<id>Connects to it
An http:// or https:// address, such as http://127.0.0.1:9222Reads the browser’s WebSocket address from /json/version, then connects

OpenOrange reads the variable from the shell environment, .dev.vars, or .env, like the R2 S3 settings.

On a cluster, set the variable on the runner pool. The runner must be able to reach the browser.

Use it

@cloudflare/puppeteer works unchanged:

import puppeteer from "@cloudflare/puppeteer";

export default {
  async fetch(request, env) {
    const browser = await puppeteer.launch(env.BROWSER);
    const page = await browser.newPage();
    await page.goto("https://example.com");
    const title = await page.title();
    await browser.close();
    return new Response(title);
  },
};

OpenOrange answers the requests that @cloudflare/puppeteer 1.x sends to Browser Rendering:

RequestWhat OpenOrange does
launch() (POST /v1/devtools/browser)Checks that the browser answers, then starts a session.
The DevTools WebSocket (/v1/devtools/browser/<sessionId>)Opens a CDP connection to the browser and passes every message through.
sessions(), history(), limits()Lists this runtime’s sessions.

OpenOrange does not limit the number of sessions. The limit of the browser or service in ORANGE_BROWSER_CDP_URL applies. limits() reports 10 concurrent sessions, the number that @cloudflare/puppeteer expects, and OpenOrange does not enforce it.

Browserbase

Each launch() opens a new WebSocket to the endpoint, and Browserbase starts a new browser for each one. So each session runs in its own browser.

When the session’s WebSocket closes, Browserbase ends that browser within about a second. Browserbase bills for the minutes that its browsers run.

Measured on a Browserbase project that allows 3 concurrent browsers:

CaseResult
launch()About 2.2 to 2.5 seconds
Open https://example.com, read its title, take a screenshot, and closeAbout 5 seconds in all
Two sessions at onceWorked
A fourth concurrent sessionFailed. Browserbase answered 429, and launch() threw.
Browserbase ended a sessionPuppeteer reported disconnected, the next call on the page threw, and sessions() no longer listed the session.
A wrong API keylaunch() threw. Browserbase answered 401.

Errors that the Worker can read name the endpoint without its user info, query, or fragment, so they do not show the API key.

Run Chromium yourself

Use this path if you do not want a hosted browser. Any headless Chrome or Chromium works, such as a chromedp/headless-shell container.

chromium --headless --remote-debugging-port=9222 &
export ORANGE_BROWSER_CDP_URL=http://127.0.0.1:9222
orange dev

All sessions share the one browser in ORANGE_BROWSER_CDP_URL. So that one Worker cannot stop the browser for the others:

  • browser.close() closes only the pages that the session opened. The browser keeps running.
  • When a session’s WebSocket closes, its pages close too.

Sessions share the browser’s default context, so they share cookies and storage. For separate profiles, give each Worker its own browser.

Deviations

Cloudflare featureOn OpenOrange
puppeteer.connect(env.BROWSER, sessionId) after browser.disconnect()Cannot rejoin. A session ends with its WebSocket.
keep_alive, location, recording, and guardrailsAccepted and ignored
Session limitsNot enforced. See Use it.

Errors

SituationWhat the Worker sees
ORANGE_BROWSER_CDP_URL is not setenv.BROWSER.fetch() throws Browser Rendering binding "BROWSER" is not supported by OpenOrange. Set ORANGE_BROWSER_CDP_URL ...
The browser does not answerlaunch() throws Unable to create new browser: code: 503: message: Browser Rendering could not reach the browser at ...
The browser refuses the DevTools WebSocket, such as a wrong key or a hosted service’s concurrency limitlaunch() throws. The WebSocket request gets 502 with Browser Rendering could not open the DevTools WebSocket at <endpoint>: Unexpected server response: <status>.
An unknown or busy sessionThe WebSocket request gets 404 or 409, and connect() throws.

Compatibility

From the latest conformance run. Scores on the compatibility page; every binding on the status page.

Browser Rendering

Pluggable browser

Runs on Your own headless Chrome or a hosted browser, through its DevTools Protocol endpoint.

@cloudflare/puppeteer 1.x runs unchanged. With a browser you run, all sessions share it and its cookies; Browserbase starts a separate browser per session. browser.close() closes only the session's pages. keep_alive, location, and recording are ignored.

Works with

Locally: Headless Chrome

Any Chrome DevTools Protocol endpoint, set in ORANGE_BROWSER_CDP_URL.

Recorded results

WorksBrowser Rendering (browser)@cloudflare/puppeteer drives a browser that Cloudflare runs.@cloudflare/puppeteer 1.x works unchanged against the Chrome in ORANGE_BROWSER_CDP_URL: pages, screenshots, and sessions, with requests at once each in their own page. browser.close() closes only that session's pages. Without the variable, fetch() throws an error that names it. 4/4 pass
What to do
Run a headless Chrome that the runner can reach, and set ORANGE_BROWSER_CDP_URL to it.
What to plan for
  • You run the browser. All sessions share it, so they share cookies and storage.
  • A session ends with its WebSocket, so connect() cannot rejoin a session after disconnect().
  • keep_alive, location, and recording are accepted and ignored.
Results
  • browser-rendering: launch() opens a page in the configured browser, reads it, and takes a PNG screenshot Pass
  • browser-rendering: two requests at once each read their own page Pass
  • browser-rendering: browser.close() ends the session, and the next launch still works Pass
  • browser-rendering: connect() to a session that is in use is refused Pass

In the feature matrix

AlternativebrowserGives the Worker a headless browser through Browser Rendering.With ORANGE_BROWSER_CDP_URL set to a Chrome DevTools Protocol endpoint (ws://, wss://, or an http:// address whose /json/version names one), the binding answers the requests of @cloudflare/puppeteer 1.x: launch(), the DevTools WebSocket of a session, sessions(), history(), and limits(). 6 files
Notes
Without ORANGE_BROWSER_CDP_URL, the binding is a stub whose calls throw Browser Rendering binding "<NAME>" is not supported by OpenOrange. Set ORANGE_BROWSER_CDP_URL to a Chrome DevTools Protocol endpoint. Sessions on one self-run browser share it and its cookies, and browser.close() closes only the session's pages. keep_alive, location, and recording are ignored, and session limits are not enforced. See docs/modules/browser-rendering.mdx.
Source
src/config/load-wrangler.ts:932-933packages/runtime-node/src/runner.ts:597-601packages/runtime-node/src/bindings/browser/browser.ts:219-326packages/runtime-node/src/bindings/unsupported.ts:13-19tests/orange-browser.test.ts:102-159docs/modules/browser-rendering.mdx:109-124