---
title: Puppeteer
description: Learn how to use Puppeteer with Cloudflare Workers for browser automation. Access Puppeteer API, manage sessions, and optimize Browser Run.
image: https://developers.cloudflare.com/dev-products-preview.png
---

[Skip to content](#%5Ftop) 

Was this helpful?

YesNo

[ Edit page ](https://github.com/cloudflare/cloudflare-docs/edit/production/src/content/docs/browser-run/puppeteer.mdx) [ Report issue ](https://github.com/cloudflare/cloudflare-docs/issues/new/choose) 

Copy page

# Puppeteer

[Puppeteer ↗](https://pptr.dev/) is one of the most popular libraries that abstract the lower-level DevTools protocol from developers and provides a high-level API that you can use to easily instrument Chrome/Chromium and automate browsing sessions. Puppeteer is used for tasks like creating screenshots, crawling pages, and testing web applications.

Puppeteer typically connects to a local Chrome or Chromium browser using the DevTools port. Refer to the [Puppeteer API documentation on the Puppeteer.connect() method ↗](https://pptr.dev/api/puppeteer.puppeteer.connect) for more information.

The Workers team forked a version of Puppeteer and patched it to connect to the Workers Browser Run API instead. After connecting, the developers can then use the full [Puppeteer API ↗](https://github.com/cloudflare/puppeteer/blob/main/docs/api/index.md) as they would on a standard setup.

Our version is open sourced and can be found in [Cloudflare's fork of Puppeteer ↗](https://github.com/cloudflare/puppeteer). The npm can be installed from [npmjs ↗](https://www.npmjs.com/) as [@cloudflare/puppeteer ↗](https://www.npmjs.com/package/@cloudflare/puppeteer):

 npm  yarn  pnpm  bun 

```
npm i -D @cloudflare/puppeteer
```

```
yarn add -D @cloudflare/puppeteer
```

```
pnpm add -D @cloudflare/puppeteer
```

```
bun add -d @cloudflare/puppeteer
```

Note

The current version is [@cloudflare/puppeteer v1.1.0 ↗](https://github.com/cloudflare/puppeteer/releases/tag/v1.1.0), based on [Puppeteer v22.13.1 ↗](https://pptr.dev/chromium-support).

## Use Puppeteer in a Worker

Once the [browser binding](https://developers.cloudflare.com/browser-run/reference/wrangler/#bindings) is configured and the `@cloudflare/puppeteer` library is installed, Puppeteer can be used in a Worker:

* [  JavaScript ](#tab-panel-5276)
* [  TypeScript ](#tab-panel-5277)

JavaScript

```

import puppeteer from "@cloudflare/puppeteer";


export default {

  async fetch(request, env) {

    const browser = await puppeteer.launch(env.MYBROWSER);

    const page = await browser.newPage();

    await page.goto("https://example.com");

    const metrics = await page.metrics();

    await browser.close();

    return Response.json(metrics);

  },

};


```

Explain Code

TypeScript

```

import puppeteer from "@cloudflare/puppeteer";


interface Env {

  MYBROWSER: Fetcher;

}


export default {

  async fetch(request, env): Promise<Response> {

    const browser = await puppeteer.launch(env.MYBROWSER);

    const page = await browser.newPage();

    await page.goto("https://example.com");

    const metrics = await page.metrics();

    await browser.close();

    return Response.json(metrics);

  },

} satisfies ExportedHandler<Env>;


```

Explain Code

This script [launches ↗](https://pptr.dev/api/puppeteer.puppeteernode.launch) the `env.MYBROWSER` browser, opens a [new page ↗](https://pptr.dev/api/puppeteer.browser.newpage), [goes to ↗](https://pptr.dev/api/puppeteer.page.goto) [https://example.com/ ↗](https://example.com/), gets the page load [metrics ↗](https://pptr.dev/api/puppeteer.page.metrics), [closes ↗](https://pptr.dev/api/puppeteer.browser.close) the browser and prints metrics in JSON.

### Keep Alive

If users omit the `browser.close()` statement, it will stay open, ready to be connected to again and [re-used](https://developers.cloudflare.com/browser-run/features/reuse-sessions/) but it will, by default, close automatically after 1 minute of inactivity. Users can optionally extend this idle time up to 10 minutes, by using the `keep_alive` option, set in milliseconds:

JavaScript

```

const browser = await puppeteer.launch(env.MYBROWSER, { keep_alive: 600000 });


```

Using the above, the browser will stay open for up to 10 minutes, even if inactive.

Note

This is an inactivity timeout, not a maximum session duration. Sessions can remain open longer than 10 minutes as long as they stay active. To keep a session open beyond the inactivity timeout, send a command at least once within your configured window (for example, every 10 minutes). Refer to [session duration limits](https://developers.cloudflare.com/browser-run/limits/#is-there-a-maximum-session-duration) for more information.

### Set a custom user agent

To specify a custom user agent in Puppeteer, use the `page.setUserAgent()` method. This is useful if the target website serves different content based on the user agent.

JavaScript

```

await page.setUserAgent(

  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",

);


```

Note

The `userAgent` parameter does not bypass bot protection. Requests from Browser Run will always be identified as a bot.

## Local debugging with headful mode (experimental)

When developing locally with `wrangler dev` or `vite dev`, Chrome runs in headless mode by default. To launch Chrome in visible (headful) mode, set the `X_BROWSER_HEADFUL` environment variable:

Terminal window

```

X_BROWSER_HEADFUL=true npx wrangler dev


```

Or with the [Cloudflare Vite plugin](https://developers.cloudflare.com/workers/vite-plugin/):

Terminal window

```

X_BROWSER_HEADFUL=true npx vite dev


```

This opens a browser window so you can watch your Puppeteer automation in real time, making it easier to debug navigation, element selection, and page interactions.

## Element selection

Puppeteer provides multiple methods for selecting elements on a page. While CSS selectors work as expected, XPath selectors are not supported due to security constraints in the Workers runtime.

Instead of using Xpath selectors, you can use CSS selectors or `page.evaluate()` to run XPath queries in the browser context:

TypeScript

```

const innerHtml = await page.evaluate(() => {

  return (

    // @ts-ignore this runs on browser context

    new XPathEvaluator()

      .createExpression("/html/body/div/h1")

      // @ts-ignore this runs on browser context

      .evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE).singleNodeValue

      .innerHTML

  );

});


```

Explain Code

Note

`page.evaluate()` can only return primitive types like strings, numbers, and booleans. Returning complex objects like `HTMLElement` will not work.

## Session management

In order to facilitate browser session management, we've added new methods to `puppeteer`:

### List open sessions

`puppeteer.sessions()` lists the current running sessions. It will return an output similar to this:

```

[

  {

    "connectionId": "2a2246fa-e234-4dc1-8433-87e6cee80145",

    "connectionStartTime": 1711621704607,

    "sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",

    "startTime": 1711621703708

  },

  {

    "sessionId": "565e05fb-4d2a-402b-869b-5b65b1381db7",

    "startTime": 1711621703808

  }

]


```

Explain Code

Notice that the session `478f4d7d-e943-40f6-a414-837d3736a1dc` has an active worker connection (`connectionId=2a2246fa-e234-4dc1-8433-87e6cee80145`), while session `565e05fb-4d2a-402b-869b-5b65b1381db7` is free. While a connection is active, no other workers may connect to that session.

### List recent sessions

`puppeteer.history()` lists recent sessions, both open and closed. It's useful to get a sense of your current usage.

```

[

  {

    "closeReason": 2,

    "closeReasonText": "BrowserIdle",

    "endTime": 1711621769485,

    "sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",

    "startTime": 1711621703708

  },

  {

    "closeReason": 1,

    "closeReasonText": "NormalClosure",

    "endTime": 1711123501771,

    "sessionId": "2be00a21-9fb6-4bb2-9861-8cd48e40e771",

    "startTime": 1711123430918

  }

]


```

Explain Code

Session `2be00a21-9fb6-4bb2-9861-8cd48e40e771` was closed explicitly with `browser.close()` by the client, while session `478f4d7d-e943-40f6-a414-837d3736a1dc` was closed due to reaching the maximum idle time (check [limits](https://developers.cloudflare.com/browser-run/limits/)).

You should also be able to access this information in the dashboard, albeit with a slight delay.

### Active limits

`puppeteer.limits()` lists your active limits:

```

{

  "activeSessions": [

    { "id": "478f4d7d-e943-40f6-a414-837d3736a1dc" },

    { "id": "565e05fb-4d2a-402b-869b-5b65b1381db7" }

  ],

  "allowedBrowserAcquisitions": 1,

  "maxConcurrentSessions": 2,

  "timeUntilNextAllowedBrowserAcquisition": 0

}


```

* `activeSessions` lists the IDs of the current open sessions
* `maxConcurrentSessions` defines how many browsers can be open at the same time
* `allowedBrowserAcquisitions` specifies if a new browser session can be opened according to the rate [limits](https://developers.cloudflare.com/browser-run/limits/) in place
* `timeUntilNextAllowedBrowserAcquisition` defines the waiting period before a new browser can be launched.

## Puppeteer API

The full Puppeteer API can be found in the [Cloudflare's fork of Puppeteer ↗](https://github.com/cloudflare/puppeteer/blob/main/docs/api/index.md).

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/browser-run/","name":"Browser Run"}},{"@type":"ListItem","position":3,"item":{"@id":"/browser-run/puppeteer/","name":"Puppeteer"}}]}
```
