Back to Blog
Identity

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_York is an obvious mismatch
  • Locale vs. language: A de-DE locale with Accept-Language: en-US alone 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-Language header 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().timeZone and Date.getTimezoneOffset() both reflect the configured timezone, including correct DST behavior
  • Locale: All Intl formatters (NumberFormat, DateTimeFormat, Collator) use the configured locale
  • Languages: navigator.language, navigator.languages, and the Accept-Language HTTP 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

RegionTimezoneLocaleLanguagesCoordinates
US EastAmerica/New_Yorken-USen-US,en40.7128,-74.0060
US WestAmerica/Los_Angelesen-USen-US,en34.0522,-118.2437
UKEurope/Londonen-GBen-GB,en51.5074,-0.1278
GermanyEurope/Berlinde-DEde-DE,de,en52.5200,13.4050
FranceEurope/Parisfr-FRfr-FR,fr,en48.8566,2.3522
JapanAsia/Tokyoja-JPja-JP,ja,en35.6762,139.6503
South KoreaAsia/Seoulko-KRko-KR,ko,en37.5665,126.9780
BrazilAmerica/Sao_Paulopt-BRpt-BR,pt,en-23.5505,-46.6333
AustraliaAustralia/Sydneyen-AUen-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:

  1. Timezone matches the expected IANA name
  2. UTC offset is correct for the current date (accounting for DST)
  3. Locale affects number and date formatting correctly
  4. Languages are in the expected priority order
  5. All values are geographically consistent with the proxy IP

Best Practices

  1. Use IANA timezone names, not UTC offsets. America/New_York handles DST correctly. UTC-5 does not.

  2. 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.

  3. Match geolocation to the proxy city, not a street address. City-level precision is sufficient and more realistic than exact coordinates.

  4. Keep locale and timezone in the same region. A ja-JP locale with Europe/London timezone is an obvious inconsistency.

  5. Let BotBrowser auto-detect when possible. The auto default produces consistent results from the proxy IP without manual configuration.

  6. Test formatting behavior, not just API values. Check that toLocaleDateString() and toLocaleString() 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.

#timezone#locale#language#identity#geolocation