Accept-Language Quality Weighting: How q-Values Fit a Browser Profile's Locale

11 min read

Picture a QA engineer debugging a localisation issue for three hours. The profile has a German proxy, a Europe/Berlin timezone and a German locale, and canvas, WebGL, audio and fonts all look right.

Then the engineer opens Chrome DevTools and looks at the request headers.

Accept-Language: en-US,en;q=0.9

That header is sent on every single request, to a German website, from a "German" browser profile. The site has picked a language from it before the page body even loads, so the "German" test was really testing the English experience.

Much of the writing about browser fingerprinting focuses on client-side vectors — canvas hashes, WebGL renderer strings, font enumeration. But the Accept-Language header is sent with the very first HTTP request, arriving at a CDN's edge before any script executes. It also carries quality factors (those q=0.9 numbers) that create a preference ordering.

By the end of this tutorial, you'll understand how Accept-Language quality weighting works, why it matters for profile consistency, and how to configure it in JustBrowser so your language headers match your proxy geography.

Prerequisites

Before we start:

  • JustBrowser installed — the 7-day trial (card required) is full access, so it's enough to test this
  • A proxy configured — residential, ISP, or datacenter, pointed at a specific country
  • Access to header inspection tools — BrowserLeaks, DevTools, or httpbin.org
  • Basic understanding of HTTP headers — you know what a request header is

If you're new to proxy configuration, the ISP proxy setup tutorial covers the basics.

Step 1: Understand the Accept-Language Syntax

Here's what a typical Accept-Language header looks like:

Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7

Breaking it down:

  • de-DE — German as spoken in Germany. No q-value means implicit q=1.0 (highest priority)
  • de;q=0.9 — Generic German, second priority
  • en-US;q=0.8 — American English, third priority
  • en;q=0.7 — Generic English, fourth priority

The q-values range from 0 to 1. Higher means "I prefer this more." When a server has content in multiple languages, it uses these weights to decide what to serve.

Here's where it gets interesting for consistency. Real browsers generate these values based on:

  1. Your operating system's language settings
  2. Browser language preferences (chrome://settings/languages)
  3. Installed language packs
  4. Manual user configuration

A German user on Windows with default settings sends something like de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7. An American user sends en-US,en;q=0.9. Consistent across millions of real users, which is why sites can rely on the header to choose a language.

Step 2: Why This Matters

Why should you care? A few reasons.

It is sent early. Before JavaScript and before any scripts run, the server can already use it to choose a language. CDNs like Cloudflare and Akamai see this header on the very first request.

It correlates with geography. Real users in Germany send German language preferences. Real users in Japan send Japanese. A Frankfurt IP sending en-US,en;q=0.9 is a mismatch worth fixing.

It correlates with navigator.languages. The JavaScript property navigator.languages should return the same list. If Accept-Language says de-DE,de,en-US,en but navigator.languages returns ["en-US", "en"], the server and the page's own JavaScript disagree about which language the visitor wants.

The q-values themselves have patterns. Real browsers use specific increments — 0.9, 0.8, 0.7 in Chrome, while Firefox spaces its values differently. Hand-typed values like q=0.85 or q=0.73 are unusual.

A profile whose locale settings agree with each other gives you localisation, QA and analytics results that match what a real visitor from that country would get.

Step 3: Check Your Current Profile's Headers

Before configuring anything, see what your profile is currently sending.

Method 1: BrowserLeaks

Open your JustBrowser profile and visit https://browserleaks.com/headers

Look for the Accept-Language line. Note the full value including all q-factors.

Method 2: Chrome DevTools

  1. Open DevTools (F12 or Cmd+Option+I)
  2. Go to the Network tab
  3. Reload any page
  4. Click on the main document request
  5. Look at Request Headers → Accept-Language

Method 3: httpbin.org

Navigate to https://httpbin.org/headers

You'll get a JSON response showing all your request headers, including Accept-Language with exact formatting.

What you're looking for: Does the Accept-Language match your proxy's geography? If you're proxied through Germany, is German (de-DE or de) the primary language? If you're proxied through Japan, is Japanese (ja or ja-JP) primary?

Step 4: Check navigator.languages Consistency

Accept-Language doesn't exist in isolation. It needs to match what JavaScript sees.

Open your browser console (F12 → Console) and run:

console.log(navigator.languages);

You'll get an array like ["de-DE", "de", "en-US", "en"].

These must match your Accept-Language header. Same languages. Same order. If Accept-Language sends de-DE,de;q=0.9,en;q=0.8 but navigator.languages returns ["en-US", "en"], the server and the page will each act on a different language, and so will your test results. It's the first thing to check on a misconfigured profile.

Step 5: Configure Language Settings in JustBrowser

JustBrowser's profile settings include language configuration that controls both the Accept-Language header and navigator.languages. Here's how to set it up correctly:

  1. Open profile settings — Click Edit on your profile
  2. Find Language/Locale settings — Usually under Browser or Identity section
  3. Set primary language — Match your proxy's country. German proxy → de-DE
  4. Set secondary languages — Add fallbacks that make sense. Most Europeans have English as a secondary language.
  5. Verify q-value generation — JustBrowser generates realistic q-values (0.9, 0.8, etc.) automatically

Example configuration for a German profile:

  • Primary: de-DE
  • Secondary: de, en-US, en
  • Generated header: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7

Example for a Japanese profile:

  • Primary: ja-JP
  • Secondary: ja, en-US, en
  • Generated header: ja-JP,ja;q=0.9,en-US;q=0.8,en;q=0.7

The pattern matters. English as a secondary language is realistic for most non-English-speaking countries. But English as the primary language from a non-English IP is inconsistent.

This doesn't need to be complicated: match the primary language to the proxy country, add English as secondary, and move on.

Step 6: Align With Other Locale Signals

Language headers don't exist alone. They're part of a locale fingerprint that includes:

Timezone. German language should pair with Europe/Berlin or Europe/Vienna. Japanese language with Asia/Tokyo. A mismatch like de-DE language with America/New_York timezone is inconsistent.

Intl API outputs. JavaScript's Intl object uses the browser's locale settings. new Intl.DateTimeFormat().resolvedOptions().locale should return something consistent with your Accept-Language primary.

Keyboard layout. QWERTZ layout expectations for German, AZERTY for French, and so on. Typing itself is a separate layer, covered in the behavioral biometrics explainer.

Currency formatting. new Intl.NumberFormat().format(1234.56) returns "1.234,56" in German locale (comma as decimal separator) vs "1,234.56" in US English.

All of these should tell a consistent story. JustBrowser's locale settings propagate to most of these — but verify them after configuration. Use the console checks above and your own header inspection.

No single tool checks every locale signal at once, so the checks are manual, one verification at a time.

For ad verification workflows where you're checking geo-targeted landing pages, this locale consistency is critical. Pair this setup with ClickzProtect to ensure you're also catching click fraud patterns that waste your ad budget.

Step 7: Test the Complete Stack

After configuring, run your verification:

BrowserLeaks Headers — Accept-Language matches expected pattern Console check — navigator.languages matches Accept-Language ordering Intl check — Intl.DateTimeFormat().resolvedOptions() agrees with the primary language Timezone — matches the proxy's geography

If any of these disagree, go back and adjust. The goal is zero inconsistencies in the locale stack.

Common Errors and How to Fix Them

Accept-Language shows en-US but proxy is non-English country

Cause: Profile was created with default US English settings and language wasn't updated when proxy changed.

Fix: In JustBrowser profile settings, change the primary language to match your proxy's geography. German proxy → de-DE primary. French proxy → fr-FR primary. Save and verify with BrowserLeaks.

Cause: Some browsers have separate settings for HTTP headers vs JavaScript APIs. Or the settings aren't propagating correctly.

Fix: In JustBrowser, the language settings should control both. If they're mismatched, check that you've saved the profile settings and relaunched the browser. If still mismatched, recreate the profile — sometimes cloned profiles inherit stale configurations.

q-values look unusual (q=0.85, q=0.73)

Cause: Manual configuration with non-standard quality factors. Real browsers use predictable increments.

Fix: Let JustBrowser generate q-values automatically. Chrome typically uses 0.9, 0.8, 0.7 decrements. Firefox uses slightly different patterns. Avoid specifying unusual decimal values by hand.

Language matches proxy but the site still behaves oddly

Cause: Language is consistent but other locale signals aren't. Timezone, Intl outputs, or keyboard layout contradict the language setting.

Fix: Run full locale verification. Check timezone matches geography. Check Intl.DateTimeFormat().resolvedOptions() output. The language header is one signal — it needs to align with the complete locale stack.

Next Steps

Once your Accept-Language configuration is solid:

  1. Document your per-profile language settings. When you're running 30+ profiles across different geos, you'll forget which is which.

  2. Test after proxy changes. If you switch a profile from a German to a French proxy, re-run geo-match so language, locale and timezone follow the new exit IP (or update them by hand).

  3. Periodic verification. Monthly spot-check with BrowserLeaks. Configurations drift, especially after browser updates.

  4. Match each profile to its purpose. Profiles for different clients or regions should carry the language settings that fit that client or region, rather than one template copied everywhere. The same goes for QA profiles, such as the ones in our guide to testing in-app review prompts.

The Accept-Language header is one of the oldest HTTP headers — defined in RFC 2616 back in 1999. It is still sent on every request, and many setups ignore it. Five minutes of configuration per profile, maybe less.

For tracking how your campaigns perform, JustAnalytics can help you monitor session quality at scale.

Frequently Asked Questions

What are q-values in the Accept-Language header?

Q-values (quality factors) are decimal weights from 0 to 1 that tell the server your language preference order. The header Accept-Language: de-DE,de;q=0.9,en;q=0.8 means German (Germany) is your top choice (implicit q=1.0), generic German is second (q=0.9), and English is third (q=0.8). Browsers generate these based on your OS language settings, installed language packs, and manual browser configuration. Sites use them to pick which language to serve, and some compare them with the navigator.languages array and timezone, so a test profile's settings should agree with each other.

Why does Accept-Language ordering matter for browser profiles?

Because the Accept-Language header is sent on every HTTP request, before JavaScript even executes. It's one of the first signals a server receives. If your proxy is in Frankfurt but your Accept-Language starts with en-US,en;q=0.9, the profile's settings disagree with each other. Real German users usually have de-DE or de as their primary language, so a site that localises on this header serves your "German" test session its English pages, and a CDN can compare the header with the request's geo-IP on the very first request.

How do I check my Accept-Language header in a browser profile?

Three methods: BrowserLeaks shows your raw Accept-Language header under the HTTP Headers section. Chrome DevTools Network tab displays request headers for any page load — look for Accept-Language in the request headers. For programmatic checks, hit a service like httpbin.org/headers which echoes back all your request headers as JSON. Test every profile after a proxy change — unless you re-run geo-match, Accept-Language won't follow the new exit IP on its own.

Does JustBrowser automatically set Accept-Language based on proxy location?

JustBrowser lets you configure locale settings per profile, including the language list that populates both navigator.languages and the Accept-Language header. You set the primary language, secondary languages, and the q-values get generated to match realistic browser behavior. The profile's timezone, locale, and language settings should all align with your proxy's geography — JustBrowser can geo-match the profile for you: geo-match (in the app, or POST /profiles/{id}/geo-match on the local API) compares the profile's timezone and language with where the proxy exits and can apply the values that agree. You can also set every value by hand.


Try JustBrowser

A multi-account browser for teams, built on a custom Chromium engine — not extension-based. Real C++ engine patches at the canvas / WebGL / audio / font / screen layer, so each profile's 40+ identity parameters are consistent with each other. Local REST API + CDP for Playwright, Puppeteer, Selenium. $9.99/month or $99.99/year. 7-day free trial, card required — cancel any time in the seven days and you are not charged. Unlimited profiles.

Get started → · How it differs from Multilogin / GoLogin / AdsPower

Ready to manage multiple accounts?

Seven days free, then $9.99/month — one plan, everything included.

We'd like to use Google Analytics, a Google service, to understand how our website is used. It sets two cookies in your browser and runs only if you click Accept. You can change your choice at any time with Cookie settings. Cookie Policy

Sign-in cookies and the cookie that remembers this choice are always on; the website needs them to work.

Google Analytics, a Google service, helps us understand how our website is used. It sets two cookies, _ga and _ga_TVZHQ99TZW. It is now onoff in this browser. If your browser sends a Global Privacy Control or Do Not Track signal, it stays off. Cookie Policy