From 29b564edabbf8f86681a69d382afb68c1a5f7212 Mon Sep 17 00:00:00 2001 From: Sofia Cardita Date: Thu, 20 Aug 2026 00:43:44 +0100 Subject: [PATCH 1/5] [Browser Run] Document session guardrails --- .../browser-run/2026-08-20-guardrails.mdx | 26 +++ .../docs/browser-run/features/guardrails.mdx | 149 ++++++++++++++++++ .../docs/browser-run/features/live-view.mdx | 53 ++++++- 3 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 src/content/changelog/browser-run/2026-08-20-guardrails.mdx create mode 100644 src/content/docs/browser-run/features/guardrails.mdx diff --git a/src/content/changelog/browser-run/2026-08-20-guardrails.mdx b/src/content/changelog/browser-run/2026-08-20-guardrails.mdx new file mode 100644 index 00000000000..462b40cb6b7 --- /dev/null +++ b/src/content/changelog/browser-run/2026-08-20-guardrails.mdx @@ -0,0 +1,26 @@ +--- +title: "Browser Run adds guardrails to restrict browser sessions" +description: Browser Run now supports guardrails, letting you restrict which hosts a browser session can reach and share read-only views of a running session. +products: + - browser-run +date: 2026-08-20 +--- + +[Browser Run](/browser-run/) now supports [guardrails](/browser-run/features/guardrails/), which let you restrict which hosts a browser session can reach. Guardrails are set when the session is acquired and cannot be changed afterwards, so agents and automation scripts stay within the boundaries you define. + +Every outbound request — including subresources like scripts, images, and fonts — is checked against the allowlist. Requests to hosts not on the list return a `403` response. + +Set them when you launch a session with Puppeteer, Playwright, or the REST API: + +```ts +const browser = await puppeteer.launch(env.MYBROWSER, { + guardrails: { + allowedDomains: ["example.com", "*.example.com"], + allowedDomainSets: ["common-cdns"], + }, +}); +``` + +Guardrails also apply to [Live View](/browser-run/features/live-view/) links. Set `guardrails` when generating a link to [share a view-only URL](/browser-run/features/live-view/#share-a-view-only-link), which streams the session but blocks navigation, input, and JavaScript evaluation. + +Refer to the [guardrails documentation](/browser-run/features/guardrails/) for hostname pattern rules, domain sets, and limits. diff --git a/src/content/docs/browser-run/features/guardrails.mdx b/src/content/docs/browser-run/features/guardrails.mdx new file mode 100644 index 00000000000..ea67dc56bf9 --- /dev/null +++ b/src/content/docs/browser-run/features/guardrails.mdx @@ -0,0 +1,149 @@ +--- +pcx_content_type: how-to +title: Guardrails +description: Restrict which hosts a Browser Run session can reach, so a browser visiting pages you do not control cannot reach anything else. +sidebar: + order: 7 + badge: Beta +products: + - browser-run +--- + +import { CURL, Details, Tabs, TabItem } from "~/components"; + +Guardrails limit which hosts a Browser Run session may reach. Every outbound request, including subresources such as scripts, fonts, and images, is checked against an allowlist. This is useful to ensure agents run in a sandboxed, controlled environment. + +Guardrails apply to Browser Sessions ([Puppeteer](/browser-run/puppeteer/), [Playwright](/browser-run/playwright/), and [CDP](/browser-run/cdp/)). They are not available for [Quick Actions](/browser-run/quick-actions/). + +Pass a policy when you acquire a session: + + + +```ts +import puppeteer from "@cloudflare/puppeteer"; + +const browser = await puppeteer.launch(env.MYBROWSER, { + guardrails: { + allowedDomains: ["example.com", "*.example.com"], + allowedDomainSets: ["common-cdns"], + }, +}); +``` + + + +```ts +import { launch } from "@cloudflare/playwright"; + +const browser = await launch(env.MYBROWSER, { + guardrails: { + allowedDomains: ["example.com", "*.example.com"], + allowedDomainSets: ["common-cdns"], + }, +}); +``` + + + + + + + +| Property | Type | Description | +| ------------------- | ---------- | ------------------------------------------------------------- | +| `allowedDomains` | `string[]` | Hostname patterns the browser may reach. Maximum 50 entries. | +| `allowedDomainSets` | `string[]` | Preset names or URLs of hostname lists. Maximum four entries. | + +Both are optional and are combined into a single allowlist. Omitting both leaves the session unrestricted. + +Guardrails are fixed when the session is acquired and cannot be changed or removed afterwards. An invalid policy rejects the request with a `400` response. + +:::note +Guardrails require the latest versions of `@cloudflare/puppeteer` or `@cloudflare/playwright`. They are not supported with [Kitesurf](/browser-run/kitesurf/). +::: + +## Hostname patterns + +Each entry in `allowedDomains` is a bare hostname, with no scheme, port, or path. A pattern may contain one `*`, which matches any sequence of characters, including dots: + +| Pattern | Matches | Does not match | +| ------------------- | --------------------------------------------------- | ------------------------------------- | +| `example.com` | `example.com` | `www.example.com`, `evil-example.com` | +| `*.example.com` | `www.example.com`, `api.v1.example.com` | `example.com`, `evilexample.com` | +| `*example.com` | `example.com`, `www.example.com`, `evilexample.com` | `example.net` | +| `api.*.example.com` | `api.v1.example.com`, `api.staging.example.com` | `api.example.com` | + +:::caution[Prefer subdomain wildcards] +A prefix wildcard such as `*example.com` also matches lookalike hostnames that an attacker can register, such as `evilexample.com`. Use `*.example.com` instead, and list the apex domain separately if you need it. +::: + +## Domain sets + +`allowedDomainSets` adds hostnames from a curated preset or from a list you host yourself, so you do not have to repeat a long allowlist on every request. One preset is available, `common-cdns`, which covers widely used package, script, and font CDNs. + +
+ +```txt +cdn.jsdelivr.net +*.jsdelivr.net +unpkg.com +cdnjs.cloudflare.com +esm.sh +esm.run +ajax.googleapis.com +fonts.googleapis.com +fonts.gstatic.com +ajax.aspnetcdn.com +aspnetcdn.com +use.typekit.net +use.fontawesome.com +kit.fontawesome.com +stackpath.bootstrapcdn.com +maxcdn.bootstrapcdn.com +cdn.tailwindcss.com +``` + +Presets are additive. Cloudflare may add hostnames to a preset, but will not remove or narrow existing entries. + +
+ +A hosted list is an HTTPS URL returning `text/plain`, with one hostname pattern per line. Blank lines and lines starting with `#` are ignored, and a single invalid line rejects the whole set. Cloudflare caches each list for one hour. + +## Deny all outbound traffic + +An explicitly empty `allowedDomains` array, with no domain sets, blocks every outbound request. This is useful when you set page content yourself and the page does not require external assets. + +```json +{ + "guardrails": { + "allowedDomains": [] + } +} +``` + +## Blocked requests + +The browser receives a `403` response for a blocked request, with headers that identify guardrails as the cause: + +```txt +HTTP/1.1 403 Forbidden +cf-mitigated: guardrails +cf-brapi-guardrails-reason: not-in-allowlist +content-type: text/plain; charset=utf-8 +``` + +## Related resources + +- [Share a view-only Live View link](/browser-run/features/live-view/#share-a-view-only-link) — restrict a Live View link so the recipient can watch a session without interacting with it. diff --git a/src/content/docs/browser-run/features/live-view.mdx b/src/content/docs/browser-run/features/live-view.mdx index 4d8f0842ffe..dba1f229ac1 100644 --- a/src/content/docs/browser-run/features/live-view.mdx +++ b/src/content/docs/browser-run/features/live-view.mdx @@ -131,9 +131,55 @@ If you have a running session and want to connect to it: 3. Copy the `devtoolsFrontendUrl` and open it in your browser. -## Get Live View URL via CDP +## Generate a Live View URL -When using Puppeteer, Playwright, or a direct WebSocket connection, you can retrieve a Live View URL programmatically using the `Cloudflare.getLiveView` CDP command: +Listing targets returns a `devtoolsFrontendUrl` for every tab, using default settings. To choose the viewing mode, extend how long the URL stays valid, or restrict what the recipient can do, you can generate a custom Live View URL using either the REST API or CDP. + +### REST API + +Use the `live_view` endpoint to generate a Live View URL: + + + +All parameters are optional. Set `mode` to `devtools`, `tab`, or `full`, and `expiresInMs` to anything up to one hour. Add `targetId` to pick a specific tab, otherwise the first active page is used. + +The response contains a `devtoolsFrontendUrl` to open in a browser or embed in an iframe, and a `webSocketDebuggerUrl` for programmatic CDP access. + +### Share a view-only link + +To let someone watch a session without interacting with it, set `guardrails` to `{ "mode": "readonly" }`: + + + +The returned link streams the session and lets the viewer inspect the page, but blocks navigation, input, and JavaScript evaluation. Navigation controls are turned off, and the browser tab title is prefixed with `READ ONLY - ` so the recipient knows the session is not interactive. + +:::caution +A view-only link is for humans watching a session, not for automation. Puppeteer and Playwright cannot drive a read-only connection, because the commands they rely on to open pages and run scripts are blocked. +::: + +### CDP + +When using Puppeteer, Playwright, or a direct WebSocket connection, you can generate a Live View URL using the `Cloudflare.getLiveView` CDP command: ```js // Create a CDP session from your page @@ -160,9 +206,10 @@ const { devtoolsFrontendUrl } = await cdp.send("Cloudflare.getLiveView", { targetId, // use the target ID from above, this is optional, if omitted the current target will be used mode: "tab", // Options: "devtools", "tab", or "full" expiresInMs: 300000, // optional, default is 5 minutes (max 1 hour) + guardrails: { mode: "readonly" }, // optional, returns a view-only URL }); console.log(`Live View URL: ${devtoolsFrontendUrl}`); ``` -Refer to the [Human in the Loop reference](/browser-run/features/human-in-the-loop/#cloudflaregetliveview) for full parameter and return details. +If you want to use Live View as part of a human operator handoff, refer to [Human in the Loop](/browser-run/features/human-in-the-loop/) for the full workflow and parameter details. From ad608f52b037411e7c7b6327942c82ba341659a0 Mon Sep 17 00:00:00 2001 From: Sofia Date: Thu, 20 Aug 2026 11:09:03 +0100 Subject: [PATCH 2/5] Apply suggestions from code review Co-authored-by: Simona Badoiu --- src/content/docs/browser-run/features/guardrails.mdx | 10 +++++----- src/content/docs/browser-run/features/live-view.mdx | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/content/docs/browser-run/features/guardrails.mdx b/src/content/docs/browser-run/features/guardrails.mdx index ea67dc56bf9..7757d222aa4 100644 --- a/src/content/docs/browser-run/features/guardrails.mdx +++ b/src/content/docs/browser-run/features/guardrails.mdx @@ -11,11 +11,11 @@ products: import { CURL, Details, Tabs, TabItem } from "~/components"; -Guardrails limit which hosts a Browser Run session may reach. Every outbound request, including subresources such as scripts, fonts, and images, is checked against an allowlist. This is useful to ensure agents run in a sandboxed, controlled environment. +Guardrails limit which hosts a Browser Run session can reach. Every outbound request, including subresources such as scripts, fonts, and images, is checked against an allowlist. This is useful to ensure agents run in a sandboxed, controlled environment. Guardrails apply to Browser Sessions ([Puppeteer](/browser-run/puppeteer/), [Playwright](/browser-run/playwright/), and [CDP](/browser-run/cdp/)). They are not available for [Quick Actions](/browser-run/quick-actions/). -Pass a policy when you acquire a session: +Set guardrails when you acquire a session: @@ -63,10 +63,10 @@ const browser = await launch(env.MYBROWSER, { | Property | Type | Description | | ------------------- | ---------- | ------------------------------------------------------------- | -| `allowedDomains` | `string[]` | Hostname patterns the browser may reach. Maximum 50 entries. | +| `allowedDomains` | `string[]` | Hostname patterns the browser can reach. Maximum 50 entries. | | `allowedDomainSets` | `string[]` | Preset names or URLs of hostname lists. Maximum four entries. | -Both are optional and are combined into a single allowlist. Omitting both leaves the session unrestricted. +Both are optional and are combined into a single allowlist. Omitting both leaves the session unrestricted. See Deny all outbound traffic (#deny-all-outbound-traffic) for the empty-array case. Guardrails are fixed when the session is acquired and cannot be changed or removed afterwards. An invalid policy rejects the request with a `400` response. @@ -91,7 +91,7 @@ A prefix wildcard such as `*example.com` also matches lookalike hostnames that a ## Domain sets -`allowedDomainSets` adds hostnames from a curated preset or from a list you host yourself, so you do not have to repeat a long allowlist on every request. One preset is available, `common-cdns`, which covers widely used package, script, and font CDNs. +`allowedDomainSets` adds hostnames from a curated preset or from a list you host yourself, so you do not have to repeat a long allowlist on every request. Cloudflare provides one preset today, `common-cdns`, covering widely used package, script, and font CDNs.
diff --git a/src/content/docs/browser-run/features/live-view.mdx b/src/content/docs/browser-run/features/live-view.mdx index dba1f229ac1..ec1dfef0378 100644 --- a/src/content/docs/browser-run/features/live-view.mdx +++ b/src/content/docs/browser-run/features/live-view.mdx @@ -133,7 +133,7 @@ If you have a running session and want to connect to it: ## Generate a Live View URL -Listing targets returns a `devtoolsFrontendUrl` for every tab, using default settings. To choose the viewing mode, extend how long the URL stays valid, or restrict what the recipient can do, you can generate a custom Live View URL using either the REST API or CDP. +Listing targets returns a `devtoolsFrontendUrl` for every tab, using default settings. To pick a viewing mode, extend the expiry, or restrict what the recipient can do, generate a custom Live View URL using either the REST API or CDP. ### REST API From dc3f7ab9b0a0b3d1daaf7c621d3400f97f7b1b10 Mon Sep 17 00:00:00 2001 From: Sofia Date: Thu, 20 Aug 2026 15:11:21 +0100 Subject: [PATCH 3/5] Apply suggestion from @simonabadoiu Co-authored-by: Simona Badoiu --- src/content/changelog/browser-run/2026-08-20-guardrails.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/changelog/browser-run/2026-08-20-guardrails.mdx b/src/content/changelog/browser-run/2026-08-20-guardrails.mdx index 462b40cb6b7..9867ff8943e 100644 --- a/src/content/changelog/browser-run/2026-08-20-guardrails.mdx +++ b/src/content/changelog/browser-run/2026-08-20-guardrails.mdx @@ -6,7 +6,7 @@ products: date: 2026-08-20 --- -[Browser Run](/browser-run/) now supports [guardrails](/browser-run/features/guardrails/), which let you restrict which hosts a browser session can reach. Guardrails are set when the session is acquired and cannot be changed afterwards, so agents and automation scripts stay within the boundaries you define. +[Browser Run](/browser-run/) now supports [guardrails](/browser-run/features/guardrails/), a per-session allowlist of hosts the browser can reach. Guardrails are set when the session is acquired and cannot be changed afterwards, so agents and automation scripts stay within the boundaries you define. Every outbound request — including subresources like scripts, images, and fonts — is checked against the allowlist. Requests to hosts not on the list return a `403` response. From e4be95054b50b98f23a33173c12afd2e823b4602 Mon Sep 17 00:00:00 2001 From: Sofia Cardita Date: Thu, 20 Aug 2026 15:25:17 +0100 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Simona Badoiu --- .../docs/browser-run/features/guardrails.mdx | 53 +++++++++++++++---- .../docs/browser-run/features/live-view.mdx | 11 ++-- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/content/docs/browser-run/features/guardrails.mdx b/src/content/docs/browser-run/features/guardrails.mdx index 7757d222aa4..f561220e0ef 100644 --- a/src/content/docs/browser-run/features/guardrails.mdx +++ b/src/content/docs/browser-run/features/guardrails.mdx @@ -66,11 +66,11 @@ const browser = await launch(env.MYBROWSER, { | `allowedDomains` | `string[]` | Hostname patterns the browser can reach. Maximum 50 entries. | | `allowedDomainSets` | `string[]` | Preset names or URLs of hostname lists. Maximum four entries. | -Both are optional and are combined into a single allowlist. Omitting both leaves the session unrestricted. See Deny all outbound traffic (#deny-all-outbound-traffic) for the empty-array case. +Both are optional and are combined into a single allowlist. Omitting both leaves the session unrestricted. See [Deny all outbound traffic](#deny-all-outbound-traffic) for the empty-array case. Guardrails are fixed when the session is acquired and cannot be changed or removed afterwards. An invalid policy rejects the request with a `400` response. -:::note +:::caution Guardrails require the latest versions of `@cloudflare/puppeteer` or `@cloudflare/playwright`. They are not supported with [Kitesurf](/browser-run/kitesurf/). ::: @@ -81,8 +81,8 @@ Each entry in `allowedDomains` is a bare hostname, with no scheme, port, or path | Pattern | Matches | Does not match | | ------------------- | --------------------------------------------------- | ------------------------------------- | | `example.com` | `example.com` | `www.example.com`, `evil-example.com` | -| `*.example.com` | `www.example.com`, `api.v1.example.com` | `example.com`, `evilexample.com` | -| `*example.com` | `example.com`, `www.example.com`, `evilexample.com` | `example.net` | +| `*.example.com` | `www.example.com`, `api.v1.example.com` | `example.com` | +| `*example.com` | `example.com`, `www.example.com` | `example.net` | | `api.*.example.com` | `api.v1.example.com`, `api.staging.example.com` | `api.example.com` | :::caution[Prefer subdomain wildcards] @@ -125,14 +125,47 @@ A hosted list is an HTTPS URL returning `text/plain`, with one hostname pattern An explicitly empty `allowedDomains` array, with no domain sets, blocks every outbound request. This is useful when you set page content yourself and the page does not require external assets. -```json -{ - "guardrails": { - "allowedDomains": [] - } -} + + +```ts +import puppeteer from "@cloudflare/puppeteer"; + +const browser = await puppeteer.launch(env.MYBROWSER, { + guardrails: { + allowedDomains: [], + }, +}); +``` + + + +```ts +import { launch } from "@cloudflare/playwright"; + +const browser = await launch(env.MYBROWSER, { + guardrails: { + allowedDomains: [], + }, +}); ``` + + + + + + ## Blocked requests The browser receives a `403` response for a blocked request, with headers that identify guardrails as the cause: diff --git a/src/content/docs/browser-run/features/live-view.mdx b/src/content/docs/browser-run/features/live-view.mdx index ec1dfef0378..4bc5e08ae1b 100644 --- a/src/content/docs/browser-run/features/live-view.mdx +++ b/src/content/docs/browser-run/features/live-view.mdx @@ -151,9 +151,14 @@ Use the `live_view` endpoint to generate a Live View URL: }} /> -All parameters are optional. Set `mode` to `devtools`, `tab`, or `full`, and `expiresInMs` to anything up to one hour. Add `targetId` to pick a specific tab, otherwise the first active page is used. +| Parameter | Type | Description | +| ------------ | -------- | ------------------------------------------------------------------------------------------------ | +| `mode` | `string` | Viewing mode: `devtools`, `tab`, or `full`. Default is `devtools`. | +| `expiresInMs`| `number` | How long the URL stays valid, in milliseconds. Default is 5 minutes, maximum is 1 hour. | +| `targetId` | `string` | Target (tab) to view. If omitted, the first active target is used. | +| `guardrails` | `object` | Restrictions for the viewer. Set `{ "mode": "readonly" }` for a view-only link. | -The response contains a `devtoolsFrontendUrl` to open in a browser or embed in an iframe, and a `webSocketDebuggerUrl` for programmatic CDP access. +All parameters are optional. The response contains a `devtoolsFrontendUrl` to open in a browser or embed in an iframe, and a `webSocketDebuggerUrl` for programmatic CDP access. ### Share a view-only link @@ -174,7 +179,7 @@ To let someone watch a session without interacting with it, set `guardrails` to The returned link streams the session and lets the viewer inspect the page, but blocks navigation, input, and JavaScript evaluation. Navigation controls are turned off, and the browser tab title is prefixed with `READ ONLY - ` so the recipient knows the session is not interactive. :::caution -A view-only link is for humans watching a session, not for automation. Puppeteer and Playwright cannot drive a read-only connection, because the commands they rely on to open pages and run scripts are blocked. +A view-only link is for humans, not automation. Puppeteer and Playwright cannot drive a read-only connection, because the commands they rely on to open pages and run scripts are blocked. ::: ### CDP From 710be97ffcfe90bcb1dbd8caa9ffdb9d3c88a5c5 Mon Sep 17 00:00:00 2001 From: Sofia Cardita Date: Thu, 20 Aug 2026 16:23:34 +0100 Subject: [PATCH 5/5] Improve docs based on feedback --- .../docs/browser-run/features/guardrails.mdx | 2 +- .../docs/browser-run/features/live-view.mdx | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/content/docs/browser-run/features/guardrails.mdx b/src/content/docs/browser-run/features/guardrails.mdx index f561220e0ef..ef64b409147 100644 --- a/src/content/docs/browser-run/features/guardrails.mdx +++ b/src/content/docs/browser-run/features/guardrails.mdx @@ -63,7 +63,7 @@ const browser = await launch(env.MYBROWSER, { | Property | Type | Description | | ------------------- | ---------- | ------------------------------------------------------------- | -| `allowedDomains` | `string[]` | Hostname patterns the browser can reach. Maximum 50 entries. | +| `allowedDomains` | `string[]` | Hostname patterns the browser may reach. Maximum 50 entries. | | `allowedDomainSets` | `string[]` | Preset names or URLs of hostname lists. Maximum four entries. | Both are optional and are combined into a single allowlist. Omitting both leaves the session unrestricted. See [Deny all outbound traffic](#deny-all-outbound-traffic) for the empty-array case. diff --git a/src/content/docs/browser-run/features/live-view.mdx b/src/content/docs/browser-run/features/live-view.mdx index 4bc5e08ae1b..fdbddb66f8c 100644 --- a/src/content/docs/browser-run/features/live-view.mdx +++ b/src/content/docs/browser-run/features/live-view.mdx @@ -133,7 +133,7 @@ If you have a running session and want to connect to it: ## Generate a Live View URL -Listing targets returns a `devtoolsFrontendUrl` for every tab, using default settings. To pick a viewing mode, extend the expiry, or restrict what the recipient can do, generate a custom Live View URL using either the REST API or CDP. +Listing targets returns a `devtoolsFrontendUrl` for every tab, using [default settings](#live-view-parameters). To pick a viewing mode, extend the expiry, or restrict what the recipient can do, generate a custom Live View URL using either the REST API or CDP. ### REST API @@ -151,14 +151,16 @@ Use the `live_view` endpoint to generate a Live View URL: }} /> -| Parameter | Type | Description | -| ------------ | -------- | ------------------------------------------------------------------------------------------------ | -| `mode` | `string` | Viewing mode: `devtools`, `tab`, or `full`. Default is `devtools`. | -| `expiresInMs`| `number` | How long the URL stays valid, in milliseconds. Default is 5 minutes, maximum is 1 hour. | -| `targetId` | `string` | Target (tab) to view. If omitted, the first active target is used. | -| `guardrails` | `object` | Restrictions for the viewer. Set `{ "mode": "readonly" }` for a view-only link. | +#### Live View parameters -All parameters are optional. The response contains a `devtoolsFrontendUrl` to open in a browser or embed in an iframe, and a `webSocketDebuggerUrl` for programmatic CDP access. +| Parameter | Type | Default | Description | +| ------------ | -------- | -------------------- | ---------------------------------------------------------------------- | +| `mode` | `string` | `devtools` | Viewing mode: `devtools`, `tab`, or `full`. | +| `expiresInMs`| `number` | `300000` (5 minutes) | How long the URL stays valid, in milliseconds. Maximum is 1 hour. | +| `targetId` | `string` | First active target | Target (tab) to view. | +| `guardrails` | `object` | None | Restrictions for the viewer. Set `{ "mode": "readonly" }` for view-only. | + +All parameters are **optional**. The response contains a `devtoolsFrontendUrl` to open in a browser or embed in an iframe, and a `webSocketDebuggerUrl` for programmatic CDP access. ### Share a view-only link