Timezone, Locale, and Language Fingerprinting: A Complete Guide
How timezone, locale, and language settings create geographic fingerprints, and how to configure them consistently for complete identity control.
Introduction
Your browser exposes geographic information through multiple channels: timezone via the Intl API, locale through number and date formatting, language via navigator.language and the Accept-Language header, and geolocation through the Geolocation API. These four properties form the geographic layer of your browser identity. When they do not align with each other or with your IP address, the inconsistency is visible.
BotBrowser provides dedicated CLI flags to configure each geographic property at the engine level. By default, BotBrowser auto-derives all geographic settings from your proxy IP, so a single --proxy-server flag produces a fully consistent geographic identity. This article covers the auto-detection system, manual override flags, and configuration strategies for multi-region workflows.
Privacy Impact
Geographic metadata is one of the most commonly checked aspects of browser identity. Tracking systems compare:
- IP geolocation vs. timezone: An IP address in Germany with a timezone of
America/New_Yorkis an obvious mismatch - Locale vs. language: A
de-DElocale withAccept-Language: en-USalone is unusual - Language vs. IP region: A Japanese IP with only English language preferences may indicate misconfiguration
- Geolocation coordinates vs. IP: If the Geolocation API returns coordinates in Tokyo but the IP resolves to New York, the conflict is clear
These checks are simple and fast, making them an early filter in any tracking pipeline. Getting geographic consistency right is a necessary foundation for any privacy-focused browsing setup.
BotBrowser's automatic geographic detection eliminates the most common source of these mismatches: manual configuration errors. When you connect through a proxy, BotBrowser detects the proxy's IP and derives timezone, locale, language, and geolocation automatically.
Technical Background
How Browsers Expose Geographic Information
Timezone: JavaScript's Intl.DateTimeFormat().resolvedOptions().timeZone returns the IANA timezone name (e.g., America/New_York). The Date object's getTimezoneOffset() method returns the UTC offset in minutes. Both must be consistent with each other and with the claimed location.
Locale: The Intl.NumberFormat().resolvedOptions().locale and Intl.DateTimeFormat().resolvedOptions().locale reveal the browser's formatting locale. This affects number formatting (comma vs. period for decimals), date formatting (DD/MM vs. MM/DD), and currency formatting.
Language: navigator.language returns the primary language, and navigator.languages returns the full list of preferred languages. The Accept-Language HTTP header sends these preferences with every request.
Geolocation: The navigator.geolocation.getCurrentPosition() API returns GPS-like coordinates. This requires user permission, but when granted, the coordinates must be plausible given the IP and timezone.
The Consistency Chain
These signals form a chain that must be internally consistent:
IP address β Country β Timezone β Locale β Languages β Geolocation
Each step must logically follow from the previous one. A proxy IP in Japan should produce:
- Timezone:
Asia/Tokyo - Locale:
ja-JP - Languages:
ja-JP,ja,en - Geolocation: coordinates in Japan (if queried)
Breaking any link in this chain creates a detectable inconsistency.
DST and UTC Offsets
Timezone configuration must account for Daylight Saving Time (DST). Using UTC offsets like UTC-5 instead of IANA timezone names like America/New_York fails when DST transitions occur. America/New_York is UTC-5 in winter and UTC-4 in summer. A fixed UTC offset does not handle this transition, creating a seasonal mismatch that reveals the timezone was manually configured.
BotBrowser uses IANA timezone names internally, ensuring correct DST behavior throughout the year.
Common Approaches and Their Limitations
Framework-Level Timezone and Locale
Playwright provides timezoneId and locale options per context:
const context = await browser.newContext({
timezoneId: 'America/New_York',
locale: 'en-US',
});
This changes Intl API behavior and navigator.language, but:
- Geolocation coordinates are not affected
- The
Accept-Languageheader may not fully align - The timezone offset in
Date.getTimezoneOffset()may not update consistently in all contexts
Manual Header Setting
Setting Accept-Language via page.setExtraHTTPHeaders() changes the HTTP header but not navigator.language or navigator.languages. The mismatch between HTTP and JavaScript values is detectable.
VPN with System Timezone
Changing your system timezone to match a VPN works but affects all applications, not just the browser. It also requires administrative access and manual intervention for each region change.
BotBrowser's Approach
Automatic Geographic Detection
By default, BotBrowser auto-derives all geographic settings from the proxy IP:
chrome --bot-profile="/path/to/profile.enc" \
--proxy-server=socks5://user:pass@jp-proxy:1080
BotBrowser detects the Japanese IP and automatically configures:
- Timezone:
Asia/Tokyo - Locale:
ja-JP - Languages:
ja-JP,ja,en - Geolocation: coordinates approximated from the IP
No additional flags are needed. The auto behavior is the default for all geographic settings.
Manual Override Flags
When you need specific geographic settings, four CLI flags provide complete control:
--bot-config-timezone (ENT Tier1): Set the IANA timezone.
--bot-config-timezone=America/New_York # Specific timezone
--bot-config-timezone=auto # Derive from IP (default)
--bot-config-timezone=real # Use system timezone
--bot-config-locale (ENT Tier1): Set the browser locale.
--bot-config-locale=en-US # Specific locale
--bot-config-locale=auto # Derive from IP/language (default)
--bot-config-languages (ENT Tier1): Set language preferences.
--bot-config-languages=en-US,en # Specific languages
--bot-config-languages=auto # Derive from IP (default)
--bot-config-location (ENT Tier1): Set geolocation coordinates.
--bot-config-location=40.7128,-74.0060 # Specific coordinates
--bot-config-location=auto # Derive from IP (default)
--bot-config-location=real # Use system GPS
Engine-Level Implementation
BotBrowser sets these values at the browser engine level, which means:
- Timezone:
Intl.DateTimeFormat().resolvedOptions().timeZoneandDate.getTimezoneOffset()both reflect the configured timezone, including correct DST behavior - Locale: All
Intlformatters (NumberFormat, DateTimeFormat, Collator) use the configured locale - Languages:
navigator.language,navigator.languages, and theAccept-LanguageHTTP header all align - Geolocation:
navigator.geolocation.getCurrentPosition()returns the configured coordinates
All values are consistent across the main thread, workers, and HTTP headers.
Configuration and Usage
Full Geographic Configuration (CLI)
chrome --bot-profile="/path/to/profile.enc" \
--proxy-server=socks5://user:pass@us-east.proxy:1080 \
--bot-config-timezone=America/New_York \
--bot-config-locale=en-US \
--bot-config-languages=en-US,en \
--bot-config-location=40.7128,-74.0060
Common Region Configurations
| Region | Timezone | Locale | Languages | Coordinates |
|---|---|---|---|---|
| US East | America/New_York | en-US | en-US,en | 40.7128,-74.0060 |
| US West | America/Los_Angeles | en-US | en-US,en | 34.0522,-118.2437 |
| UK | Europe/London | en-GB | en-GB,en | 51.5074,-0.1278 |
| Germany | Europe/Berlin | de-DE | de-DE,de,en | 52.5200,13.4050 |
| France | Europe/Paris | fr-FR | fr-FR,fr,en | 48.8566,2.3522 |
| Japan | Asia/Tokyo | ja-JP | ja-JP,ja,en | 35.6762,139.6503 |
| South Korea | Asia/Seoul | ko-KR | ko-KR,ko,en | 37.5665,126.9780 |
| Brazil | America/Sao_Paulo | pt-BR | pt-BR,pt,en | -23.5505,-46.6333 |
| Australia | Australia/Sydney | en-AU | en-AU,en | -33.8688,151.2093 |
Playwright Integration
const { chromium } = require('playwright-core');
(async () => {
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--proxy-server=socks5://user:pass@us-east.proxy:1080',
'--bot-config-timezone=America/New_York',
'--bot-config-locale=en-US',
'--bot-config-languages=en-US,en',
'--bot-config-location=40.7128,-74.0060',
],
headless: true,
});
const context = await browser.newContext();
const page = await context.newPage();
const tz = await page.evaluate(() =>
Intl.DateTimeFormat().resolvedOptions().timeZone
);
const langs = await page.evaluate(() => navigator.languages);
const locale = await page.evaluate(() =>
Intl.NumberFormat().resolvedOptions().locale
);
console.log('Timezone:', tz); // America/New_York
console.log('Languages:', langs); // ['en-US', 'en']
console.log('Locale:', locale); // en-US
await browser.close();
})();
Multi-Region Setup
For workflows that span multiple regions:
const regions = [
{
profile: '/profiles/us-user.enc',
proxy: 'socks5://user:pass@us.proxy:1080',
timezone: 'America/New_York',
locale: 'en-US',
languages: 'en-US,en',
location: '40.7128,-74.0060',
},
{
profile: '/profiles/de-user.enc',
proxy: 'socks5://user:pass@de.proxy:1080',
timezone: 'Europe/Berlin',
locale: 'de-DE',
languages: 'de-DE,de,en',
location: '52.5200,13.4050',
},
{
profile: '/profiles/jp-user.enc',
proxy: 'socks5://user:pass@jp.proxy:1080',
timezone: 'Asia/Tokyo',
locale: 'ja-JP',
languages: 'ja-JP,ja,en',
location: '35.6762,139.6503',
},
];
for (const cfg of regions) {
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
`--bot-profile=${cfg.profile}`,
`--proxy-server=${cfg.proxy}`,
`--bot-config-timezone=${cfg.timezone}`,
`--bot-config-locale=${cfg.locale}`,
`--bot-config-languages=${cfg.languages}`,
`--bot-config-location=${cfg.location}`,
],
headless: true,
});
// Run region-specific tasks...
await browser.close();
}
Verification
After configuring geographic settings, verify all properties:
const page = await context.newPage();
// Timezone
const tz = await page.evaluate(() =>
Intl.DateTimeFormat().resolvedOptions().timeZone
);
// Timezone offset (should be correct for current date/DST)
const offset = await page.evaluate(() => new Date().getTimezoneOffset());
// Locale from number formatting
const locale = await page.evaluate(() =>
Intl.NumberFormat().resolvedOptions().locale
);
// Languages
const lang = await page.evaluate(() => navigator.language);
const langs = await page.evaluate(() => navigator.languages);
// Date formatting (locale-specific)
const dateFormat = await page.evaluate(() =>
new Date().toLocaleDateString()
);
// Number formatting (locale-specific)
const numFormat = await page.evaluate(() =>
(1234567.89).toLocaleString()
);
console.log('Timezone:', tz);
console.log('Offset:', offset);
console.log('Locale:', locale);
console.log('Language:', lang);
console.log('Languages:', langs);
console.log('Date format:', dateFormat);
console.log('Number format:', numFormat);
Confirm that:
- Timezone matches the expected IANA name
- UTC offset is correct for the current date (accounting for DST)
- Locale affects number and date formatting correctly
- Languages are in the expected priority order
- All values are geographically consistent with the proxy IP
Best Practices
-
Use IANA timezone names, not UTC offsets.
America/New_Yorkhandles DST correctly.UTC-5does not. -
Set languages in priority order. A user in Germany would typically have
de-DE,de,en. Including English as a secondary language is realistic for most regions. -
Match geolocation to the proxy city, not a street address. City-level precision is sufficient and more realistic than exact coordinates.
-
Keep locale and timezone in the same region. A
ja-JPlocale withEurope/Londontimezone is an obvious inconsistency. -
Let BotBrowser auto-detect when possible. The
autodefault produces consistent results from the proxy IP without manual configuration. -
Test formatting behavior, not just API values. Check that
toLocaleDateString()andtoLocaleString()produce region-appropriate formats.
Frequently Asked Questions
Does BotBrowser handle DST transitions correctly?
Yes. BotBrowser uses IANA timezone names internally, which encode DST rules. Date.getTimezoneOffset() returns the correct offset for any date, including transitions.
Can I set timezone per context in Playwright?
Playwright's timezoneId option per context changes JavaScript Intl behavior. BotBrowser's --bot-config-timezone flag sets the timezone at the engine level. Both approaches work, but the BotBrowser flag also ensures HTTP-level consistency.
What happens if I use auto-detection without a proxy? Without a proxy, BotBrowser uses your real public IP for auto-detection. The geographic settings will match your actual location.
Can I set just the timezone and let the rest auto-detect?
Yes. You can override individual settings. For example, setting only --bot-config-timezone while leaving locale, languages, and location on auto works. However, ensure the timezone is consistent with the auto-detected values.
Does --bot-config-location require user permission? BotBrowser sets the geolocation values that are returned when a page requests geolocation permission. The user permission prompt behavior is controlled by the browser context settings.
What language format does --bot-config-languages accept?
A comma-separated list of BCP 47 language tags: en-US,en,fr-FR,fr. The order determines priority, which is reflected in navigator.languages and the Accept-Language header.
Can I use these flags without --bot-profile?
The geographic flags work independently, but without a profile, you do not have fingerprint protection. Always use --bot-profile as the foundation.
Does the Accept-Language header update when I set --bot-config-languages?
Yes. The Accept-Language header on every HTTP request reflects the configured language list, including proper quality values (q-values) for priority ordering.
Summary
Timezone, locale, language, and geolocation form the geographic layer of your browser identity. BotBrowser auto-derives all geographic settings from your proxy IP by default, producing a consistent identity without manual configuration. For specific requirements, the --bot-config-timezone, --bot-config-locale, --bot-config-languages, and --bot-config-location flags provide full control at the engine level.
For proxy setup, see Proxy Configuration. For browser identity, see Browser Brand Switching and User Agent Control and Client Hints. For multi-region workflows, see Multi-Account Browser Isolation.