How camofox-browser Proxy Integration with GeoIP Automatic Locale Detection Works
camofox-browser automatically detects a client’s locale, timezone, and geolocation from the IP address of the configured proxy, then passes these values to Playwright’s browser context to ensure the automated session geographically matches the proxy server.
The camofox-browser project synchronizes browser fingerprinting with proxy infrastructure by wiring proxy Pools into Playwright’s launch sequence. This integration eliminates manual locale configuration by leveraging the proxy’s exit IP to drive automatic geographic detection. Below is the complete technical flow as implemented in the jo-inc/camofox-browser repository.
Proxy Pool Initialization and Session Support
When the server starts, it reads the CONFIG.proxy object populated from lib/config.js and initializes a connection manager using createProxyPool() (see server.js lines 409-418).
The proxy pool exposes a canRotateSessions boolean that indicates whether the provider supports per-context session rotation. For back-connect providers such as Decodo or BrightData, the pool can embed a unique session ID into the proxy username, enabling each browser tab to acquire a distinct IP address and independent geographic fingerprint.
Launch-Time Proxy Selection and the GeoIP Flag
When a client request triggers a new browser tab, the server requests a launch proxy from the pool:
const launchProxy = proxyPool
? proxyPool.getLaunchProxy(
proxyPool.canRotateSessions
? `browser-${crypto.randomUUID().replace(/-/g, '').slice(0, 12)}`
: undefined
)
: undefined;
(See server.js lines 564-574.)
If a proxy is returned, the server sets geoip: !!launchProxy in the launch payload (lines 592-607). This boolean flag signals downstream logic to apply GeoIP-based locale configuration.
Automatic Locale and Timezone Configuration
Before invoking browser.newContext(), the server assembles contextOptions. When the geoip flag is true, the options object is automatically populated with default locale and timezone values that align with the proxy’s expected IP range:
const contextOptions = {
// Additional permissions and viewport settings...
};
if (geoip) {
contextOptions.locale = 'en-US';
contextOptions.timezoneId = 'America/Los_Angeles';
contextOptions.geolocation = { latitude: 37.7749, longitude: -122.4194 };
}
contextOptions.proxy = normalizePlaywrightProxy(sessionProxy);
const context = await browser.newContext(contextOptions);
(See server.js lines 723-744.)
These initial values act as placeholders; Playwright subsequently overrides them based on the actual geographic data associated with the proxy’s public IP.
Playwright’s Native Geo-IP Handling
Playwright’s engine detects the proxy field in the context options and routes all TCP connections through the specified proxy server. By observing the exit IP of the proxy hop, Playwright automatically infers the client’s public IP and adjusts the browser’s internal navigator.language, Intl.DateTimeFormat().resolvedOptions().timeZone, and navigator.geolocation values to match the IP’s geographic location. No additional instrumentation is required; the mere presence of a working proxy triggers Playwright’s Geo-IP resolution path.
Per-Context Session Rotation for Isolated IPs
For providers that return canRotateSessions: true, camofox-browser enables per-tab geographic isolation. The system generates a unique session identifier for each tab and embeds it into the proxy credentials via buildSessionUsername() in lib/proxy.js (lines 72-94):
export const decodoProvider = {
name: 'decodo',
canRotateSessions: true,
buildSessionUsername(baseUsername, { country, sessionId }) {
const parts = [`user-${sanitizeBackconnectValue(baseUsername)}`];
if (country) parts.push(`country-${sanitizeBackconnectValue(country)}`);
if (sessionId) parts.push(`session-${sanitizeBackconnectValue(sessionId)}`);
return parts.join('-');
},
};
Each tab therefore connects through a different back-connect session, yielding independent IP addresses and unique Geo-IP detection results per browser context.
Implementation Examples
Basic Server Start with Auto Geo-IP
Configure environment variables for a back-connect provider:
# .env
PROXY_STRATEGY=back_connect
PROXY_BACKCONNECT_HOST=gate.proxy.com
PROXY_BACKCONNECT_PORT=7000
PROXY_USERNAME=myuser
PROXY_PASSWORD=mypass
PROXY_COUNTRY=US
Initialize the pool in server.js:
import { createProxyPool } from './lib/proxy.js';
import CONFIG from './lib/config.js';
const proxyPool = createProxyPool(CONFIG.proxy);
if (proxyPool) {
console.log('Proxy pool created', {
mode: proxyPool.mode,
canRotate: proxyPool.canRotateSessions,
});
}
When handling a new tab request:
const launchProxy = proxyPool
? proxyPool.getLaunchProxy(`browser-${crypto.randomUUID().slice(0, 12)}`)
: undefined;
const contextOptions = {
permissions: ['geolocation'],
locale: launchProxy ? 'en-US' : undefined,
timezoneId: launchProxy ? 'America/Los_Angeles' : undefined,
geolocation: launchProxy
? { latitude: 37.7749, longitude: -122.4194 }
: undefined,
proxy: launchProxy ? normalizePlaywrightProxy(launchProxy) : undefined,
};
const context = await browser.newContext(contextOptions);
Playwright exposes the correct navigator.language, timezone, and geolocation values corresponding to the proxy’s public IP.
Per-Tab Session Isolation
Request a fresh session for each tab to ensure independent Geo-IP data:
const sessionId = `ctx-${userId}-${Date.now()}`;
const launchProxy = proxyPool
? proxyPool.getLaunchProxy(sessionId)
: undefined;
// Each tab now uses a distinct IP address from the provider’s pool
const context = await browser.newContext({
proxy: normalizePlaywrightProxy(launchProxy),
});
Summary
- Proxy Pool Creation:
createProxyPool()inlib/proxy.jsinitializes the connection manager and detects session rotation support viacanRotateSessions. - GeoIP Flag: In
server.js, thegeoipboolean is set to true whenever a launch proxy is retrieved, triggering automatic locale configuration. - Context Options: Default
locale,timezoneId, andgeolocationvalues are injected intocontextOptionsbeforebrowser.newContext()is called. - Playwright Automation: Playwright automatically translates the proxy’s exit IP into browser-exposed locale, timezone, and geolocation values.
- Session Rotation: Back-connect providers use
buildSessionUsername()to embed unique session IDs, enabling per-tab IP isolation and independent geographic detection.
Frequently Asked Questions
How does camofox-browser detect the correct locale without manual configuration?
The system relies on Playwright’s built-in Geo-IP capabilities. When a proxy is configured in the browser context, Playwright routes traffic through that proxy, detects the public IP address of the exit node, and automatically sets the browser’s navigator.language, timezone, and geolocation to match the IP’s geographic region.
What is the purpose of the canRotateSessions flag in the proxy pool?
The canRotateSessions boolean indicates whether the proxy provider supports back-connect rotation. When true, camofox-browser generates a unique session ID for each browser tab and embeds it into the proxy username via buildSessionUsername(), ensuring each tab receives a different IP address from the provider’s pool.
Which source files handle the proxy and GeoIP logic?
The main implementation spans three files: lib/proxy.js contains the pool factory and provider-specific logic including createProxyPool() and buildSessionUsername(); lib/config.js provides the CONFIG.proxy object; and server.js orchestrates the launch flow, sets the geoip flag, and builds the contextOptions passed to Playwright.
Does camofox-browser support static proxy configurations?
Yes. While the system optimizes for back-connect providers with session rotation, it also supports static proxies. If canRotateSessions is false, the getLaunchProxy() method returns the same proxy configuration for every context, and Playwright still performs automatic Geo-IP detection based on that single exit IP.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →