Guardrails (2)
Last updated Sep 26, 2026 Guardrails limit a Browser Run session's HTTP and HTTPS requests to permitted hostnames.
This allows you to:
- Keep automation focused — Limit each session to hostnames needed for its task.
- Support page dependencies — Include required third-party APIs, scripts, images, and fonts.
- Run self-contained pages — Prevent external HTTP and HTTPS requests.
Session guardrails apply to browser sessions created with Puppeteer, Playwright, or Chrome DevTools Protocol (CDP). They are unavailable for Quick Actions.
Set up guardrails
Add the hostname the browser will visit and any hostnames required for redirects, APIs, scripts, images, or fonts. For common or shared hostnames, use a domain set instead of listing each hostname individually.
Set guardrails when you start a new session. The policy remains fixed for the lifetime of that session.
Choose a property based on how you maintain the allowlist:
| Property | Type | Use when | Limit |
|---|---|---|---|
allowedDomains |
string[] |
Your workflow needs a short, stable list with exact hostname control. | 50 entries |
allowedDomainSets |
string[] |
Many sessions share a longer list, or your team maintains one centrally. | Four entries |
Both properties are optional and form one allowlist. If you omit both, HTTP and HTTPS requests remain unrestricted.
Check policy requirements
Before starting a session, make sure your guardrail policy meets these requirements:
- Add no more than 50 entries to
allowedDomains. - Add no more than four entries to
allowedDomainSets. - Write hostname patterns without a scheme, port, or path.
- Use no more than one wildcard in each hostname pattern.
If a policy does not meet these requirements, Browser Run rejects the session request with a 400 response.
Start a guarded session with Puppeteer
This function starts a session that permits example.com, its subdomains, and hostnames from the common-cdns domain set.
The example assumes a browser binding named MYBROWSER.
src/index.jsjs
import puppeteer from "@cloudflare/puppeteer";
export async function startGuardedSession(env) {
const browser = await puppeteer.launch(env.MYBROWSER, {
guardrails: {
allowedDomains: ["example.com", "*.example.com"],
allowedDomainSets: ["common-cdns"],
},
});
return browser;
}
src/index.tsts
import puppeteer from "@cloudflare/puppeteer";
interface Env {
MYBROWSER: Fetcher;
}
export async function startGuardedSession(env: Env) {
const browser = await puppeteer.launch(env.MYBROWSER, {
guardrails: {
allowedDomains: ["example.com", "*.example.com"],
allowedDomainSets: ["common-cdns"],
},
});
return browser;
}
Playwright accepts the same guardrails object through its launch() options.
REST API
Use the REST API to acquire a guarded session outside Workers. This request assumes $ACCOUNT_ID is set and $CLOUDFLARE_API_TOKEN has Browser Rendering Write permission.
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/browser-run/devtools/browser" \
--request POST \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--json '{
"guardrails": {
"allowedDomains": [
"example.com",
"*.example.com"
],
"allowedDomainSets": [
"common-cdns"
]
}
}'
Compatibility
Use @cloudflare/puppeteer 1.4.0 or later. Use @cloudflare/playwright 1.3.6 or later.
Guardrails are not supported with Kitesurf.
Add allowed hostnames
Use allowedDomains to specify which hostnames the browser can request.
Each entry must contain only a hostname. Do not include a protocol such as https://, a port such as :443, or a path such as /api.
example.comallows onlyexample.com.*.example.comallows subdomains such aswww.example.comandapi.example.com, but notexample.com.
You can use one * wildcard in each entry to match variations of a hostname:
| 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 |
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.
Use a domain set
Domain sets help you reuse shared hostname lists across sessions. The allowedDomainSets property accepts the common-cdns set name and HTTPS URLs.
Allow common CDN hostnames
Use the Cloudflare-maintained common-cdns set when your page depends on assets served by common content delivery network (CDN) hostnames:
{
"allowedDomains": ["example.com"],
"allowedDomainSets": ["common-cdns"]
}
Cloudflare maintains the common-cdns set and may change it over time. Use allowedDomains or a hosted hostname list when you need a fixed set of permitted hostnames.
Use a hosted hostname list
Use an HTTPS URL for hostname patterns specific to your pages and dependencies:
{
"allowedDomainSets": ["https://example.com/browser-run-hostnames.txt"]
}
For example, browser-run-hostnames.txt could contain:
example.com
*.example.com
# Third-party API
api.example.net
The hosted list must meet these requirements:
| Requirement | Value |
|---|---|
| Protocol | HTTPS |
| Content type | text/plain |
| Line format | One hostname pattern per line |
| Comments | Lines starting with # and blank lines are ignored |
| Validation | One invalid line rejects the entire hosted list |
Cloudflare caches a hosted list for up to one hour. Updates after the cache refresh affect only newly started sessions, not existing sessions.
Block all web requests
An empty allowedDomains array blocks all HTTP and HTTPS requests. Use it for self-contained pages, such as rendering inline HTML to a screenshot or PDF.
Inline content can render, but the browser cannot request external APIs or assets. Do not include any domain sets with this policy.
Use this policy object as the guardrails value across supported integrations:
{
"allowedDomains": []
}
Verify blocked requests
Use Puppeteer to request a hostname outside the allowlist. This example checks the response status and guardrail headers.
import puppeteer from "@cloudflare/puppeteer";
export async function verifyGuardrails(env) {
const browser = await puppeteer.launch(env.MYBROWSER, {
guardrails: {
allowedDomains: ["example.com"],
},
});
try {
const page = await browser.newPage();
const response = await page.goto("https://example.org");
const status = response?.status();
const headers = response?.headers() ?? {};
if (
status !== 403 ||
headers["cf-mitigated"] !== "guardrails" ||
headers["cf-brapi-guardrails-reason"] !== "not-in-allowlist"
) {
throw new Error("Expected Browser Run guardrails to block the request");
}
} finally {
await browser.close();
}
}
import puppeteer from "@cloudflare/puppeteer";
interface Env {
MYBROWSER: Fetcher;
}
export async function verifyGuardrails(env: Env) {
const browser = await puppeteer.launch(env.MYBROWSER, {
guardrails: {
allowedDomains: ["example.com"],
},
});
try {
const page = await browser.newPage();
const response = await page.goto("https://example.org");
const status = response?.status();
const headers = response?.headers() ?? {};
if (
status !== 403 ||
headers["cf-mitigated"] !== "guardrails" ||
headers["cf-brapi-guardrails-reason"] !== "not-in-allowlist"
) {
throw new Error("Expected Browser Run guardrails to block the request");
}
} finally {
await browser.close();
}
}
A blocked request returns a 403 response with these headers:
| Header | Value | Meaning |
|---|---|---|
cf-mitigated |
guardrails |
Confirms guardrails blocked the request |
cf-brapi-guardrails-reason |
not-in-allowlist |
Requested hostname was not permitted |
Use guardrails with Live View
Session guardrails remain active when you use Live View. The { mode: "readonly" } Live View setting controls viewer interaction and does not change the session hostname allowlist.
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/browser-run/features/guardrails/#page","headline":"Guardrails","description":"Restrict HTTP and HTTPS requests by destination hostname.","url":"https://developers.cloudflare.com/browser-run/features/guardrails/","inLanguage":"en","image":"https://developers.cloudflare.com/browser-run/features/guardrails/og.png?v=b378a2df794d8e3f","dateModified":"2026-09-26","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: Temporarily hand off browser control to a human operator for authentication, sensitive actions, or tasks that are difficult to fully automate. title: Human in the Loop image: https://developers.cloudflare.com/browser-run/features/human-in-the-loop/og.png?v=43082c0cf2d6e8e2
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/browser-run/llms.txt
Use this file to discover all available pages before exploring further.
Docs