Back to Blog
Network

Dynamic Proxy Switching: Per-Context Proxy Configuration Guide

How to configure runtime proxy switching and per-context proxy settings for managing multiple network identities simultaneously.

Introduction

Many privacy workflows require more than a single proxy for an entire browser session. You may need different proxies for different accounts, geographic regions, or target domains. Static proxy configuration, where one proxy serves all traffic for the browser's lifetime, is limiting in these scenarios.

BotBrowser supports dynamic proxy switching through Playwright and Puppeteer's context APIs. Each browser context can route traffic through a different proxy with its own geographic identity, and you can create and close contexts at runtime without restarting the browser. This article covers per-context proxy configuration, proxy rotation strategies, and multi-proxy setups for complex workflows.

Privacy Impact

When multiple accounts or identities share a single proxy, their traffic patterns are linked by IP address. A tracking system that observes the same IP accessing multiple accounts at similar times can correlate those accounts. Even if the fingerprint profiles are different, the shared network identity creates a connection.

Per-context proxy assignment solves this. Each context routes through a distinct proxy, so traffic from different identities never shares an IP. Combined with BotBrowser's per-context fingerprint isolation, each context presents a completely independent identity: different fingerprint, different IP, different geographic metadata.

Dynamic proxy switching is also important for workflows that span multiple regions. An identity that needs to appear in the US for one task and in Germany for another can switch proxies between tasks, with BotBrowser automatically adjusting timezone, locale, and language to match.

Technical Background

Browser Contexts and Network Isolation

A browser context in Playwright or Puppeteer is an isolated browsing session within a single browser instance. Each context has its own cookies, localStorage, sessionStorage, and cache. Playwright extends this isolation to network configuration: each context can have its own proxy settings.

When you create a context with browser.newContext({ proxy: ... }), all traffic from that context routes through the specified proxy. Other contexts in the same browser instance are unaffected. This is true network isolation at the context level.

Static vs. Per-Context Proxy Configuration

Static configuration sets a proxy at browser launch with --proxy-server. All contexts in that browser instance share the same proxy:

chrome --bot-profile="/path/to/profile.enc" \
       --proxy-server=socks5://user:pass@proxy:1080

Per-context configuration sets proxies when creating individual contexts:

const context = await browser.newContext({
  proxy: { server: 'socks5://proxy:1080', username: 'user', password: 'pass' },
});

Per-context proxies override the browser-level proxy for that specific context. If no context-level proxy is specified, the context inherits the browser-level proxy.

How BotBrowser Extends Context Proxies

Standard Chromium supports per-context proxies through Playwright's API, but it does not automatically derive geographic settings per context. BotBrowser extends this behavior: when a per-context proxy is configured, BotBrowser auto-derives timezone, locale, and language from the proxy's IP for that specific context.

This means each context gets a complete geographic identity without manual configuration of every setting.

Common Approaches and Their Limitations

Launching Separate Browser Instances

The simplest approach to multi-proxy setups is launching a separate browser instance per proxy:

# Instance 1
chrome --bot-profile="/profiles/profile-a.enc" \
       --proxy-server=socks5://proxy-us:1080 \
       --user-data-dir="/tmp/session-1"

# Instance 2
chrome --bot-profile="/profiles/profile-b.enc" \
       --proxy-server=socks5://proxy-de:1080 \
       --user-data-dir="/tmp/session-2"

This works but consumes more memory and CPU. Each browser instance has its own renderer processes, GPU process, and utility processes. Running 10 instances uses significantly more resources than running 10 contexts within a single instance.

Framework-Level Proxy Rotation

Some frameworks provide proxy rotation through page.route() or interceptor APIs. These approaches modify traffic at the JavaScript/CDP level, which means:

  • The initial connection may use the wrong proxy before the interceptor activates
  • WebSocket connections may not be covered
  • DNS prefetching may leak through the original proxy path

Per-context proxies configured at the browser engine level do not have these timing issues.

Proxy Middleware Servers

Running a local proxy middleware that handles rotation adds infrastructure complexity and a single point of failure. The browser connects to the local middleware, which forwards to different upstream proxies. This works but introduces latency and requires maintaining additional software.

BotBrowser's Approach

Per-Context Proxy with Auto-Detection

BotBrowser's per-context proxy support (ENT Tier1) provides full geographic identity per context:

const { chromium } = require('playwright-core');

const browser = await chromium.launch({
  executablePath: '/path/to/botbrowser/chrome',
  args: [
    '--bot-profile=/path/to/profile.enc',
  ],
  headless: true,
});

// US context - BotBrowser auto-derives US geographic settings
const usContext = await browser.newContext({
  proxy: { server: 'socks5://us-proxy:1080', username: 'user', password: 'pass' },
});

// Germany context - BotBrowser auto-derives German geographic settings
const deContext = await browser.newContext({
  proxy: { server: 'socks5://de-proxy:1080', username: 'user', password: 'pass' },
});

Each context automatically receives timezone, locale, and language settings matching its proxy's geographic location.

Manual Geographic Overrides Per Context

When you need specific geographic settings for a context, combine the proxy with Playwright's locale and timezone options:

async function createGeoContext(browser, config) {
  return browser.newContext({
    proxy: {
      server: config.proxyUrl,
      username: config.user,
      password: config.pass,
    },
    locale: config.locale,
    timezoneId: config.timezone,
  });
}

const usCtx = await createGeoContext(browser, {
  proxyUrl: 'socks5://us-east:1080',
  user: 'u', pass: 'p',
  locale: 'en-US',
  timezone: 'America/New_York',
});

const jpCtx = await createGeoContext(browser, {
  proxyUrl: 'socks5://jp-tokyo:1080',
  user: 'u', pass: 'p',
  locale: 'ja-JP',
  timezone: 'Asia/Tokyo',
});

Proxy Rotation Between Requests

For workflows that need IP diversity across navigations, create a new context per rotation:

const proxyPool = [
  { server: 'socks5://proxy-1:1080', username: 'user', password: 'pass' },
  { server: 'socks5://proxy-2:1080', username: 'user', password: 'pass' },
  { server: 'socks5://proxy-3:1080', username: 'user', password: 'pass' },
];

async function navigateWithRotation(browser, url, index) {
  const proxy = proxyPool[index % proxyPool.length];
  const context = await browser.newContext({ proxy });
  const page = await context.newPage();

  try {
    await page.goto(url);
    const content = await page.content();
    return content;
  } finally {
    await context.close();
  }
}

// Each call uses a different proxy
for (let i = 0; i < 10; i++) {
  await navigateWithRotation(browser, 'https://example.com', i);
}

Closing the context after each use ensures clean state: no cookies, cache, or connection reuse between rotations.

Session-Sticky Proxy Assignment

For workflows where each account needs a persistent proxy across multiple page loads, keep the context open:

const accounts = [
  { name: 'account-a', proxy: 'socks5://proxy-us:1080' },
  { name: 'account-b', proxy: 'socks5://proxy-uk:1080' },
  { name: 'account-c', proxy: 'socks5://proxy-de:1080' },
];

const contexts = {};

for (const account of accounts) {
  contexts[account.name] = await browser.newContext({
    proxy: { server: account.proxy, username: 'user', password: 'pass' },
  });
}

// Each account uses its dedicated context and proxy
const pageA = await contexts['account-a'].newPage();
await pageA.goto('https://example.com/login');
// ... login and perform tasks with account A's proxy

const pageB = await contexts['account-b'].newPage();
await pageB.goto('https://example.com/login');
// ... login and perform tasks with account B's proxy

Configuration and Usage

CLI-Level Default with Per-Context Override

Set a default proxy at the CLI level, then override per context:

const browser = await chromium.launch({
  executablePath: '/path/to/botbrowser/chrome',
  args: [
    '--bot-profile=/path/to/profile.enc',
    // Default proxy for contexts that don't specify one
    '--proxy-server=socks5://default-proxy:1080',
  ],
  headless: true,
});

// This context uses the default proxy
const defaultCtx = await browser.newContext();

// This context overrides with a specific proxy
const specialCtx = await browser.newContext({
  proxy: { server: 'socks5://special-proxy:1080', username: 'u', password: 'p' },
});

Combining with BotBrowser CLI Flags

For the highest geographic consistency, combine per-context proxies with BotBrowser's CLI-level flags:

const browser = await chromium.launch({
  executablePath: '/path/to/botbrowser/chrome',
  args: [
    '--bot-profile=/path/to/profile.enc',
    '--bot-local-dns',
    '--bot-webrtc-ice=google',
  ],
  headless: true,
});

The --bot-local-dns and --bot-webrtc-ice flags apply globally and protect all contexts from DNS and WebRTC leaks.

Puppeteer Multi-Instance Approach

Puppeteer does not support per-context proxies in the same way as Playwright. For Puppeteer, use separate browser instances:

const puppeteer = require('puppeteer-core');

async function createInstance(profile, proxy, timezone, locale) {
  return puppeteer.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      `--bot-profile=${profile}`,
      `--proxy-server=${proxy}`,
      `--bot-config-timezone=${timezone}`,
      `--bot-config-locale=${locale}`,
      `--user-data-dir=${'/tmp/session-' + Math.random().toString(36).slice(2)}`,
    ],
    headless: true,
    defaultViewport: null,
  });
}

const usBrowser = await createInstance(
  '/profiles/us.enc', 'socks5://us-proxy:1080',
  'America/New_York', 'en-US'
);

const deBrowser = await createInstance(
  '/profiles/de.enc', 'socks5://de-proxy:1080',
  'Europe/Berlin', 'de-DE'
);

Verification

After setting up dynamic proxy switching, verify each context independently:

async function verifyContext(context, expectedCountry) {
  const page = await context.newPage();

  // Check IP
  await page.goto('https://httpbin.org/ip');
  const ip = await page.textContent('body');
  console.log(`${expectedCountry} IP:`, ip);

  // Check timezone
  const tz = await page.evaluate(() =>
    Intl.DateTimeFormat().resolvedOptions().timeZone
  );
  console.log(`${expectedCountry} Timezone:`, tz);

  // Check locale
  const locale = await page.evaluate(() =>
    Intl.NumberFormat().resolvedOptions().locale
  );
  console.log(`${expectedCountry} Locale:`, locale);

  await page.close();
}

await verifyContext(usContext, 'US');
await verifyContext(deContext, 'DE');

Confirm that each context shows a different IP, timezone, and locale. Verify that no traffic from one context leaks into another.

Best Practices

  1. Close contexts when done. Open contexts consume memory. Close them after use to free resources.

  2. Use Playwright for per-context proxies. Playwright's native per-context proxy support is more efficient than running separate browser instances.

  3. Match proxy geography with locale. Always align timezone, locale, and language with the proxy's location. BotBrowser handles this automatically when using per-context proxies.

  4. Set --bot-local-dns and --bot-webrtc-ice globally. These flags protect all contexts from network leaks regardless of proxy configuration.

  5. Use different fingerprint profiles for different accounts. While per-context proxies isolate network identity, using different profiles per account provides fingerprint isolation as well.

  6. Do not use page.authenticate() with BotBrowser. The page.authenticate() API can interfere with BotBrowser's geographic auto-detection. Use embedded credentials in the proxy URL instead.

Frequently Asked Questions

Can I change a context's proxy after creation? No. Proxy settings are fixed when the context is created. To switch proxies, close the current context and create a new one.

How many contexts can run simultaneously? There is no hard limit. Each context consumes memory proportional to its open pages and cached resources. In practice, hundreds of contexts can run within a single browser instance on a machine with sufficient RAM.

Do per-context proxies support SOCKS5H? Yes. All proxy protocols supported by --proxy-server are also supported in per-context proxy configuration.

Does BotBrowser auto-detect geography for per-context proxies? Yes (ENT Tier1). When you set a per-context proxy, BotBrowser detects the proxy's IP and derives timezone, locale, and language automatically for that context.

Can I mix static and per-context proxies? Yes. Set a default proxy with --proxy-server at launch. Contexts that specify their own proxy override the default. Contexts without a proxy setting inherit the browser-level default.

What happens to cookies when I close a context? All cookies, localStorage, and cache for that context are discarded. If you need cookie persistence, export cookies before closing the context.

Is per-context proxy switching supported in Puppeteer? Puppeteer has limited per-context proxy support. For full per-context proxy control, use Playwright. For Puppeteer, launch separate browser instances with different --proxy-server values.

Summary

Dynamic proxy switching enables complex multi-identity workflows within a single browser instance. BotBrowser's per-context proxy support, combined with automatic geographic detection, provides each context with a complete network identity: unique IP, matching timezone, appropriate locale, and consistent language settings.

For proxy fundamentals, see Proxy Configuration. For preventing network leaks across all contexts, combine with DNS Leak Prevention and WebRTC Leak Prevention. For complete identity isolation, see Multi-Account Browser Isolation.

#proxy#dynamic#switching#per-context#network