Skip to content

Session recording

Beta

When browser automation fails or behaves unexpectedly, it can be difficult to understand what happened. Session recording captures DOM changes, mouse and keyboard events, and page navigation as structured JSON events — not a video — so it is lightweight and easy to inspect. Recordings are powered by rrweb and are opt-in per session.

Enable session recording

Pass recording: true to puppeteer.launch() or playwright.launch():

TypeScript
import puppeteer from "@cloudflare/puppeteer";
interface Env {
MYBROWSER: Fetcher;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const browser = await puppeteer.launch(env.MYBROWSER, { recording: true });
const page = await browser.newPage();
await page.goto("https://example.com");
// ... your automation steps ...
const sessionId = browser.sessionId();
await browser.close();
return new Response(`Session recorded: ${sessionId}`);
},
};

Enable with CDP endpoint

When connecting to Browser Run from any environment using the CDP endpoint, add recording=true as a query parameter to the WebSocket URL:

wss://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/browser-rendering/devtools/browser?recording=true&keep_alive=600000

For example, to enable session recording in an MCP client, add recording=true to the --wsEndpoint URL in your client configuration:

{
"mcpServers": {
"browser-rendering": {
"command": "npx",
"args": [
"-y",
"chrome-devtools-mcp@latest",
"--wsEndpoint=wss://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/browser-rendering/devtools/browser?recording=true&keep_alive=600000",
"--wsHeaders={\"Authorization\":\"Bearer <API_TOKEN>\"}"
]
}
}
}

For other MCP clients and CDP usage with Puppeteer or Playwright, refer to the CDP documentation.

View recordings

After a session closes, its recording is available in the Cloudflare dashboard under Browser Run > Runs. Select the recording icon next to a session to open the recording viewer, where you can scrub through the timeline and replay what happened during the session.

If a session opened multiple tabs, the recording viewer shows a tab selector dropdown in the top-right corner of the replay area. Use it to switch between the recorded tabs and view the activity for each one individually.

Go to Browser Run Runs

Retrieve a recording via API

You can also retrieve a recording programmatically using the session ID. Use browser.sessionId() to capture the session ID before closing the browser, then pass it to the recordings endpoint.

Terminal window
curl https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/browser-rendering/recording/<SESSION_ID> \
-H "Authorization: Bearer <API_TOKEN>"

A successful response looks similar to the following:

{
"sessionId": "e26d4660-5b78-4761-b82f-c6b5bad5a925",
"duration": 4380,
"events": {
"target-1": [],
"target-2": []
}
}

The keys in events (such as target-1, target-2) are CDP targets. In the context of session recording, each target typically corresponds to a browser tab. A session that opened multiple tabs will have one target per tab, and each target's value is an independent rrweb event array for that tab.

Replay a recording

Each value in events is a standard rrweb event array and can be passed directly to rrweb-player to self-host a replay UI with a timeline scrubber and playback controls.

Tabs replay independently — to replay a multi-tab session, render one player per target, or build a UI that lets the user switch between targets (similar to the tab selector in the dashboard recording viewer).

Limits

  • Recordings are retained for 30 days after the session ends and automatically deleted.
  • Recording is opt-in. It is not enabled by default.
  • Session recording is available with Browser Sessions via launch() and the CDP endpoint. It is not available with Quick Actions.
  • The minimum recording duration is 1 second. Sessions shorter than 1 second will not produce a viewable recording.
  • The maximum recording duration is 2 hours.

rrweb limitations

Session recording uses rrweb, which records DOM state and events rather than pixels. This approach is lightweight but has the following limitations:

  • Canvas elements — The content of <canvas> elements is not captured. The element itself appears in the recording as a blank placeholder.
  • Cross-origin iframes — Content inside cross-origin <iframe> elements is not recorded. Same-origin iframes are recorded normally.
  • Video and audio — The DOM structure of <video> and <audio> elements is captured, but media playback state and content are not.
  • WebGL — WebGL rendering is not captured.
  • Input fields — The content of all input fields is masked by default and will not be visible in the replay.
  • Large or complex pages — Pages with frequent DOM mutations (for example, pages with real-time data feeds or heavy animations) can generate a high volume of events, which increases the size of the recording.