Dynamic proxy switching for separate browser contexts
Choose between a proxy per context, runtime proxy switching, and separate browser instances, then verify each context before you rely on its route.
BotBrowser Team
Prefer the maintained product doc?
This article has a matching page in the docs center. Use the docs for the canonical setup flow, current flags, and long-term reference.
Pick the unit that owns the route
A browser rarely needs one network path for everything it does. A team that tests a regional service, keeps several authorized accounts apart, or compares a page from two locations needs each of those jobs to leave through its own route. A single proxy applied when the browser starts is simple and predictable, but it ties every page to the same exit address for as long as the browser stays open. Dynamic proxy switching is one answer to that limit, and it is only one of three, so the first decision is which unit should own the route: the whole browser instance, a browser context, or a context whose route changes while it stays open.
A browser context is an isolated session inside one browser instance. It keeps its own cookies, local storage, and cache, and it is cheap to create and discard compared with starting another browser. Playwright describes contexts as isolated browser profiles, and Puppeteer exposes the same idea through its own context API. Because the context is already the boundary for stored state, it is also a natural boundary for a network route: work that must stay separate should share neither storage nor an exit address.
The table compares the three options. Memory cost and state isolation are the usual deciding factors, and the license tier decides which options are available to you at all.
| Option | Best when | Cost and isolation | Documented requirement |
|---|---|---|---|
| One proxy per context, set at creation | Each account or region keeps one route for the whole life of its context | Contexts share one browser instance, so they use less memory than separate instances, and each keeps its own cookies and storage | ENT Tier3 license for BotBrowser's per-context proxy |
| Runtime switch of an existing context | One workflow moves between regions, or a route must be replaced without discarding the open context | The context keeps its pages and stored state while the route changes | ENT Tier3 license and the BotBrowser.setBrowserContextProxy command |
| Separate browser instance per proxy | You need the strongest separation, or per-context routing is not part of your tier | Each instance carries its own full browser footprint and its own user data directory | A launch-level --proxy-server value, described in the proxy configuration guide |
Three questions usually settle the choice. If the route has to change while the same context stays open, use runtime switching. If each account keeps one route for its whole life, assign the proxy when the context is created, because a route that never changes is easier to review and to explain later. If your license does not include per-context routing, or you want the widest separation, start one browser instance per proxy and accept the extra memory.
Memory is the cost that most often decides between contexts and separate instances, and it depends on what the pages do. Open tabs, media, extensions, and downloads all add to it, so a result from an empty page says little about a production job. Measure with your real authorized workload, set conservative limits, and leave room for short peaks instead of copying a capacity figure from a demo or from another team's setup.
Some teams rotate routes with request interception inside the automation framework, or with a local proxy service that forwards to different upstream proxies. Those designs can work, but they add moving parts to run and monitor. BotBrowser's documentation also notes that timezone alignment depends on a proxy set through --proxy-server or the per-context proxy, not on framework-only proxy options, so keep the route on the browser side when regional signals matter to your task.
Assign a proxy when the context is created
BotBrowser's documented way to give a context its own route is to supply the proxy when the context is created. In Puppeteer that is the proxyServer option of createBrowserContext, with credentials embedded in the proxy URL. A context created without its own proxy inherits the proxy route and geographic identity of the launch profile, so any context that must not use the browser-level route needs a proxy of its own.
const client = await browser.target().createCDPSession();
const ctx = await browser.createBrowserContext({
proxyServer: 'socks5://user:pass@us-proxy.example.com:1080',
});
await client.send('BotBrowser.setBrowserContextFlags', {
browserContextId: ctx._contextId,
botbrowserFlags: ['--bot-profile=path/to/profile.enc', '--proxy-ip=203.0.113.1'],
});
const page = await ctx.newPage();
After creation, BotBrowser detects the exit IP of that context's proxy and sets timezone, locale, and language for the context from it. If you already know the exit IP, pass it with --proxy-ip and the lookup is skipped. When a context has its own route and you also declare the exit IP, provide both at creation time: the documentation warns that applying --proxy-ip only after creation can let geographic resolution start before the address declaration is available. Send the flag update before the first page is created in the context, as in the example above.
Settings left on auto follow the context's proxy, while explicit values are resolved independently for each context and each setting. That is useful when a team deliberately fixes one value, such as an interface language required by a test plan, and lets the others follow the route. Treat every fixed value as a decision with an owner. An old override keeps looking valid after the route changes, and it is easy to forget that it was set.
Playwright documents its own proxy option for new contexts, and it is the right reference for what that option does inside Playwright. BotBrowser's per-context documentation is written around the context creation option and the flags shown above, so confirm the region values on your own test page before you rely on automatic alignment in a Playwright setup. The Playwright page linked in the sources is the authority for the framework behavior.
Credentials deserve the same care as the route. The documented command and creation option take the proxy URL with embedded credentials, so keep the credentials there instead of replacing them with a separate page.authenticate() call. Supply them from your secret manager at job start, keep them out of source files, tickets, and run records, and rotate them through their owner. The article on proxy authentication and credential hygiene covers that process in more detail.
Closing a context discards its cookies, local storage, and cache, and a later context starts clean. That is the behavior you want when each task should begin from nothing, and a problem when an account session has to outlive the context. If a session must continue, decide before closing what you export, where it is stored, and who may read it. The per-context proxy guide describes how to keep one purpose, one context, and one route together.
A short route register keeps these assignments reviewable. For each context, record a readable label for the route, the task it serves, its owner, the intended region when one matters, and the reference to the secret that holds its credentials. Use a label that describes the purpose instead of copying a hostname into many jobs, so the provider can rotate an endpoint without every template changing. When the purpose changes, create a new assignment rather than silently reusing an old label, because reviews that rely on the label stop being reliable.
Switch the proxy of a live context
Runtime switching exists for workflows in which the same context needs a different route later. With an ENT Tier3 license, BotBrowser documents the BotBrowser.setBrowserContextProxy CDP command, which changes the proxy of an existing browser context. The command takes the browserContextId and a proxyServer URL with embedded credentials, and optionally proxyIp, proxyBypassList, and proxyBypassRgx.
const client = await browser.target().createCDPSession();
await client.send('BotBrowser.setBrowserContextProxy', {
browserContextId: ctx._contextId,
proxyServer: 'socks5://user:pass@uk-proxy.example.com:1080',
proxyIp: '198.51.100.1',
});
await page.goto(nextUrl);
The command must be sent on a browser-level session. In Puppeteer that is browser.target().createCDPSession(), and in Playwright it is browser.newBrowserCDPSession(). A page-level session does not expose the BotBrowser domain, and a "command not found" error is the usual symptom of using the wrong session. The same error can appear when the license does not include the feature.
When you call the command, BotBrowser updates the proxy configuration of that context, re-detects the exit IP unless you supplied proxyIp, and reconfigures timezone, locale, and language to match the new location. The command completes after the updated geographic state is applied, and later requests from that context use the new proxy. For that reason, await the command before you start any navigation that depends on the new location. Keep proxy changes, flag updates, and proxy clearing for one context in sequence, and wait for each command to resolve before you send the next one.
Plan for the handover itself. Pages that are already loaded keep working, and requests that were in flight when you switched finish on the previous proxy. Only new requests use the new one, so the change is neither instant nor free of overlap. In practice, let pending navigation settle and pause timers or polling on the page before you switch, then navigate again once the command resolves. A page that stayed open across the switch should not be counted as having come from the new route.
Supplying proxyIp removes the one-time lookup before the first navigation that follows a switch. Use it when you already have the exit address from your provider, and make sure it is the real exit address of that proxy, because the geographic settings are derived from the value you give. For a proxy that has an address in only one family, the documentation allows ipv4_none or ipv6_none to declare the missing family. Without proxyIp, expect the first load after a switch to take a little longer, which is normal and not by itself a failure.
The BotBrowser.clearBrowserContextProxy command removes the override and returns the context to the browser-level proxy, or to a direct connection when none is configured. A direct connection may be exactly what you do not want, so check what the launch configuration provides before you use it. A workflow that must never leave without a proxy should switch to another approved proxy instead of clearing.
The proxyBypassList option takes a semicolon-separated list of hosts that connect directly, and proxyBypassRgx takes an RE2 pattern for URLs that should do the same. Those requests do not use the proxy at all. Keep the list short, record it as part of the route assignment, and test one host that should connect directly and one that should not.
Teams that want a different exit for each job can create a new context for every rotation or switch one context between jobs. A new context starts with no cookies or cache, so nothing is carried over. A switched context keeps its stored state, which means a signed-in account session continues from a new address. Whether that is acceptable is a decision for the account owner, and a site may treat the change differently from what you expect. The article on proxy failover and user-visible continuity discusses what people see when a route changes in the middle of a session.
Verify each context separately
The goal of verification is to see, for every context, its own exit address and the region values you expect. Run the check separately per context after creation, and again after every switch. Do not generalize from one context to another, because each context has its own route and its own geographic settings.
For the address, open a page on a service you control, or one your organization approves, that reports the source address of a request, and compare it with the exit address your proxy provider lists for that proxy. A match shows that traffic left through the intended route. A different address means the proxy setting was not applied to that context. If you configured direct-connection rules, check one host that should connect directly and one that should not.
For timezone, locale, and language, have a test page you control display the values the browser reports and compare them with the region of the exit address. For a context with an explicit override, compare against the override instead. The article on timezone, locale, and language explains how these values relate to each other and why a mismatch is more informative than any single value.
After a switch, wait for the command to resolve, then open a new page or reload the existing one before you check again. Treat pages that were loaded before the switch as stale. Then confirm the other direction: the contexts you did not switch should still report their original addresses. That check is the evidence that a switch on one context did not change the route of another.
Keep the evidence narrow. A short record with the route label, the purpose of the context, the observed exit address, the region values, the result, and the time is enough. Do not store page content, account data, or credentials to prove that a route was applied.
When a check fails, work through the causes in order. Confirm that the command was sent on a browser-level session and that your license includes the feature. Confirm that the proxy accepts a connection from a plain tool outside the browser, which separates a provider problem from a configuration problem. Check whether an explicit timezone, locale, or language override is set for that context, since overrides take priority over the values derived from the route. Check that a proxyIp value you supplied is the real exit address of that proxy. Change one thing at a time, and repeat the check after each change.
Limits to plan around
A proxy switch changes the network path of one context and nothing else. The quality of that path belongs to the proxy provider: the reputation of the exit address, its speed and stability, and how accurately its location is registered. Switching to a poor proxy gives a context a poor route, so measure the provider with your own tests before you build a workflow on it.
A shared exit address is one linkage signal among several. Separate addresses reduce one kind of connection between accounts, and they do not control how a site relates identities through account data, payment details, behavior, device-related values, or the people who operate them. Nothing here guarantees that a site accepts, ignores, or fails to correlate your contexts. Use per-context routing for authorized work such as your own accounts, regional quality checks, and privacy research, and follow the terms of each service.
Decide in advance what happens when a switch fails. A provider endpoint can be unavailable and a runtime command can be rejected. For a workflow that needs a specific route, stop the job and record the failure rather than continue on the previous route, unless the owner has approved that fallback in writing.
BotBrowser supports per-context proxies and, with an ENT Tier3 license, the BotBrowser.setBrowserContextProxy CDP command, which changes the proxy of an existing browser context at runtime and re-detects the exit IP to update that context's timezone, locale, and language. That helps when one workflow has to move between regions, or when a route must be replaced without discarding the open context. BotBrowser cannot supply or vet proxies, improve a proxy's IP reputation or speed, move requests already in flight off the previous proxy, or guarantee how a site correlates or accepts identities beyond the network path.
Public sources
Related Articles
Take BotBrowser from research to production
The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.