network.allowed_hosts to limit which destinations a browser session can reach through Kernel’s egress proxy. The proxy refuses unlisted destinations for navigations, subresources, fetch() and XHR, and WebSockets. This isn’t full VM-level network isolation; traffic that doesn’t use the proxy isn’t filtered. See limitations.
An allowlist limits the damage a prompt injection can do: an agent tricked into sending data to an attacker’s server gets a refusal instead of a connection.
Create a browser with an allowlist
Set the allowlist when you create the browser:start_url. The API rejects a start_url that the list doesn’t allow. The browser’s network.allowed_hosts returns the list as Kernel stored it: trimmed, lowercased, and deduplicated.
If Kernel can’t start a browser that enforces the allowlist, the request fails with a 500 rather than starting a browser without it.
Supported entries
You can provide up to 100 entries, each no longer than 253 characters:- Exact hostnames:
shop.example.com - A single leading wildcard:
*.example.commatches subdomains at any depth, such asapi.example.comandcdn.eu.example.com, but notexample.comitself. Add both entries to allow a domain and its subdomains. - Public IPv4 addresses:
8.8.8.8 - Bracketed public IPv6 addresses:
[2001:4860:4860::8888] - Canonical public CIDRs:
8.8.4.0/24or2001:4860::/32
xn--) form.
IP and CIDR entries only match destinations written as an IP address, such as https://8.8.8.8/. They never match a hostname that resolves into the range, so add the hostname itself.
Kernel rejects:
- An empty list (
[]). Omitallowed_hostsfor unfiltered egress. - Wildcards over any public suffix, including provider domains such as
*.github.io,*.cloudfront.net,*.vercel.app, and*.herokuapp.com. List the specific host instead, such asd111111abcdef8.cloudfront.net. - Private, loopback, link-local, and other reserved IP ranges. To reach private services, use private networking.
- Entries that overlap
network.private_hosts. Private hosts bypass Kernel’s egress proxy, so an allowlist can’t filter them. - Chrome policies in
chrome_policythat can send traffic around the egress proxy:WebRtcIPHandlingandWebRtcIPHandlingUrlfor WebRTC, andDnsOverHttpsMode(unless it’soff),DnsOverHttpsTemplates,DnsOverHttpsTemplatesWithIdentifiers, andDnsOverHttpsSaltfor secure DNS. - Non-canonical forms, such as CIDRs with host bits set or IPv4-mapped IPv6 addresses.
When a destination is blocked
Kernel’s egress proxy answers a request for an unlisted destination with a403 and sets the X-Kernel-Proxy-Error header to network_policy_denied. A navigation shows a Kernel error page that names the blocked host and says that retrying won’t help, so an agent reading the page can tell a policy refusal from a website’s own 403. The proxy also refuses blocked fetch(), XHR, and subresource requests, and WebSocket handshakes fail.
The proxy’s 403 and header aren’t guaranteed to be visible to page JavaScript. For cross-origin fetch() and XHR, CORS can hide the denial response and produce net::ERR_FAILED. Cross-origin subresources can be rejected by Chromium’s opaque response blocking (ORB): an image receiving the HTML denial page can fail with net::ERR_BLOCKED_BY_ORB.
With network telemetry enabled, proxy_error classification depends on Chromium exposing the branded response in Network.responseReceived. When it does, the event’s code is network_policy_denied. Cross-origin requests blocked by CORS or ORB can instead appear only as network_loading_failed events, without the denial status or header. Those generic error strings can also occur for unrelated failures, so they don’t prove an allowlist denial on their own.
HTTPS connections on ports other than 443 get a bare 403 without the page, which Chromium reports as net::ERR_TUNNEL_CONNECTION_FAILED. Those refusals also appear only as network_loading_failed, and WebSocket handshake failures aren’t classified as proxy_error. See proxy errors for the other codes.
CAPTCHAs
The automatic CAPTCHA solver in stealth mode works with an allowlist. It only talks to Kernel’s own browser infrastructure, which is always allowed. The CAPTCHA itself loads from the CAPTCHA provider, not from the site that shows it. If a site you allow uses a CAPTCHA, add the provider’s domains as well, or the challenge can’t load and the solver has nothing to solve. For example:- reCAPTCHA:
www.google.comandwww.gstatic.com - hCaptcha:
hcaptcha.comand*.hcaptcha.com - Cloudflare Turnstile:
challenges.cloudflare.com
network_policy_denied proxy_error event when Chromium exposes the branded response, or only a network_loading_failed event when it doesn’t. Don’t rely on proxy_error alone to find every blocked provider resource.
Change the allowlist during a session
Replace the allowlist on a running browser withupdate() (PATCH /browsers/{id_or_name}). Chromium keeps running and open pages stay loaded:
- New requests to destinations the new list doesn’t allow are refused within a few seconds, and Kernel closes open connections to them within about 30 seconds. Connections to destinations it still allows, such as an open WebSocket, stay open.
- During a Kernel deploy, an open connection to a removed host can survive for up to 10 minutes. New requests to it are still refused within a few seconds.
- A
start_urlin the same update must be allowed by the new list. Kernel navigates to it only after the new list is in effect. - Omitting
allowed_hostsleaves the list unchanged, and[]is rejected. - If the update fails with a
500, retry it. The new list might already apply to some requests.
network, only allowed_hosts can change after creation, and only on a browser created with an allowlist. update() can replace or remove the list, but adding one to a browser created without it returns 400 not_supported, so create the browser with an allowlist when you’ll need one.
To remove the allowlist and return to unfiltered egress, set allowed_hosts to null. Adding it back also returns 400 not_supported; create a new browser with an allowlist instead:
Chromium can show a page from its HTTP cache without sending a request. After you remove a host, a page Chromium cached earlier might still render, even though new requests to that host are refused.
Use allowlists with browser pools
Setnetwork.allowed_hosts on a browser pool to give every browser it creates the same allowlist. The same entry rules, start_url validation, Chrome policy restrictions, and proxy-only enforcement apply. Allowlisted pools require proxy v3 and won’t fall back to a proxy that can’t enforce the list.
network.allowed_hosts returns the allowlist for that lease. An acquisition’s start_url override must also be allowed. While you hold the browser, you can replace or remove its existing allowlist with browsers.update() as shown above. You still can’t add a list to a browser created without one or add it back after removal.
To change the pool’s baseline for future acquisitions, update the pool:
network object, unlike a browser update. Omit network to leave it unchanged. If you provide it, include every network setting you want to keep, including private_hosts and proxy_routes. Omitting allowed_hosts from that replacement removes the pool’s allowlist; network: {} clears the entire network configuration. An empty allowlist ([]) is invalid. You can add an allowlist to a pool that doesn’t have one because its unfiltered browsers are replaced rather than given a list in place.
Changing the pool’s allowlist automatically replaces idle browsers, without requiring discard_all_idle: true. Browsers with a stale allowlist aren’t handed out while the pool refills. Already acquired browsers keep their current list until release; a pool update doesn’t change their egress immediately.
On release with reuse: true, Kernel restores the current pool allowlist before returning the browser to the pool. If it can’t safely restore and enforce that baseline, it destroys the browser and the pool creates a replacement. This includes a browser whose allowlist you removed when the pool still requires one: the list isn’t added back to that browser. Per-lease allowlist changes don’t carry over to the next acquisition. Reuse still preserves cookies, storage, and other browser state; it isn’t isolation between users.
Keep the allowlist out of the agent’s reach
An allowlist only constrains a browser if the agent driving it can’t change it. Anyone holding your Kernel API key can update or remove the allowlist, or create a new browser without one. Set and change allowlists from your own code, and don’t give the agent your API key or connect it to the Kernel MCP server. On allowlisted browsers, CDP context creation specifyingproxyServer or proxyBypassList is refused, including empty strings or direct proxy settings. This includes Playwright’s browser.newContext({ proxy }). Ordinary context creation without those proxy parameters remains permitted. Browsers without an allowlist retain normal custom-proxy context behavior.
Don’t give the constrained agent code execution inside the browser VM. Code running there can send traffic directly and disable the VM-side CDP restriction. The allowlist isn’t full VM-level network isolation. See limitations.
Limitations
- Enforcement covers Chromium’s HTTP(S) and WebSocket traffic through Kernel’s egress proxy, not all traffic leaving the browser VM. This isn’t full VM-level network isolation:
- Code executing inside the browser VM, including through process execution, can send traffic directly and disable the VM-side CDP restriction.
- Extensions with the
proxypermission can override Chromium’s proxy settings and switch to direct connections. - DNS lookups that don’t go through the proxy aren’t filtered, such as Chromium’s lookups for hosts in
network.private_hosts. Requests through the egress proxy are resolved by the proxy, and Kernel rejects the Chrome policies that turn on secure DNS for allowlisted browsers.
- CDP context creation with
proxyServerorproxyBypassListis refused on allowlisted browsers, including empty/direct settings and Playwright’sbrowser.newContext({ proxy }). Ordinary context creation is permitted. Browsers without an allowlist retain normal custom-proxy context behavior; code executing inside the VM can disable this restriction. - Destinations in
network.private_hosts, including the default private ranges, are reached directly and aren’t filtered. If the session doesn’t need private routes, setprivate_hoststo[]to remove those browser proxy bypasses. This doesn’t restrict direct traffic from code executing inside the VM. - Requests to Kernel’s own browser infrastructure, which features such as CAPTCHA solving and telemetry depend on, are always allowed.