JustBrowser
Tutorials11 min read

Accept-Language Quality Weighting: How q-Values Betray a Cloned Browser Profile

JustBrowser Platform Team·

Picture a scraping engineer debugging a detection issue for three hours. Fingerprints are passing — canvas, WebGL, audio, fonts, the whole stack. German residential proxy. Europe/Berlin timezone. German locale.

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

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

Sent on every single request. To a German website. From a "German" browser profile. The detection system doesn't need JavaScript fingerprinting to flag this session. It has the signal before the page body even loads.

Here's what frustrates me about most antidetect tutorials: everyone obsesses over client-side fingerprint vectors — canvas hashes, WebGL renderer strings, font enumeration. But the Accept-Language header fires on the very first HTTP request. Arrives at Cloudflare's edge before your fingerprint scripts even execute. And it carries quality factors (those q=0.9 numbers) that create a preference ordering as unique as a fingerprint itself.

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

Prerequisites

Before we start:

  • JustBrowser installed — the 7-day trial 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 detection. 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. Detection systems know what real distributions look like — and they know what doesn't fit.

Step 2: Why This Matters for Detection

Why should you care? A few reasons.

It fires early. Before JavaScript. Before fingerprint scripts. Before any client-side evasion can kick in. 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. Frankfurt IP sending en-US,en;q=0.9? That's a signal.

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"], detection scripts catch this immediately.

The q-values themselves have patterns. Real browsers use specific increments — 0.9, 0.8, 0.7 in Chrome. Or 0.9, 0.8, 0.5 in Firefox. Made-up values like q=0.85 or q=0.73 look synthetic.

Accept-Language inconsistencies can be one of the first-pass filters anti-fraud systems use. Cheap to check, high signal. They don't need to wait for complex fingerprinting if the HTTP headers are already wrong.

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"], detection scripts will flag this inconsistency immediately.

CreepJS specifically checks for this. So does FingerprintJS Pro. It's a basic consistency check that catches poorly configured antidetect setups.

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

Unpopular opinion: most people overcomplicate this. You don't need a linguistics degree. Match the primary language to the proxy country. Add English as secondary. Done. 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. Covered in detail in the keyboard layout fingerprinting post, but the short version: QWERTZ layout expectations for German, AZERTY for French, etc.

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. Run through Pixelscan or CreepJS and check the locale/language sections specifically.

It would help to have a single tool that checked all locale signals at once. There isn't one, so the checks are manual, one verification at a time. Annoying? Yes. Necessary? Also yes.

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 Pixelscan — No locale inconsistency warnings CreepJS — Lies detection shows no language/locale mismatches IPHey — Language settings align with geo-IP location

If any of these show warnings, 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 antidetect 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. Don't manually specify unusual decimal values — they're a signal.

Language matches proxy but still getting flagged

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. See the timezone mismatch guide for the full picture.

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 and Pixelscan. Configurations drift, especially after browser updates.

  4. Consider language diversity. If all your profiles have identical secondary language patterns (all with en-US;q=0.8,en;q=0.7), that's a weak fingerprint signal. Real user populations have variation. Mix it up.

The Accept-Language header is one of the oldest HTTP headers — defined in RFC 2616 back in 1999. Twenty-seven years later, it's still a reliable detection signal because so many antidetect setups ignore it entirely. Five minutes of configuration per profile. Maybe less. Worth it.

For tracking which profiles are performing well across your campaigns, 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. Detection systems compare these weights against your navigator.languages array and timezone — mismatches flag synthetic profiles.

Why does Accept-Language ordering matter for antidetect browsers?

Because the Accept-Language header fires 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, you're immediately inconsistent. Real German users have de-DE or de as their primary language. Detection systems like Cloudflare and Akamai check this header against geo-IP on the very first request — before fingerprint scripts even load.

How do I check my Accept-Language header in an antidetect browser?

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: one click (or POST /profiles/{id}/geo-match) sets timezone, locale and language to the proxy's exit IP, and rotating proxies get their timezone matched automatically at launch. You can also set every value by hand.


Try JustBrowser

Native Chromium antidetect browser — not extension-based. Real C++ engine patches at the canvas / WebGL / audio / font / screen layer, so 40+ identity parameters are genuine, not faked. REST API 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