Do Not Track and GPC: How Privacy Signals Become Fingerprints
Two browser privacy settings, Do Not Track and Global Privacy Control, were designed to protect users. Because so few browsers turn them on, they have also become fingerprinting signals.
The other half of the story is consistency. Each signal travels two ways: as an HTTP header the server receives and as a JavaScript property page scripts can read. A privacy tool that sets navigator.globalPrivacyControl to true but never sends the Sec-GPC header tells a page script one thing and the server another, and the header is the part a site is expected to act on. Your opt-out may simply never arrive.
This guide is for privacy-minded users who want their settings to work, for site owners testing whether they honour GPC, and for teams who keep several browser profiles for legitimate work and want each one configured on purpose.
Check your own fingerprint first. We built a public checker at justbrowser.app/fingerprint-checker, client-side with no signup. It compares the WebRTC candidates against your public IP, and it checks whether the IP's location has a UTC offset that agrees with your browser's timezone. Worth running before you read the rest of this, so the signals below have your numbers attached to them.
What We're Building
By the end of this tutorial, you'll understand:
- How DNT headers and GPC signals work at the technical level
- Why these privacy features can increase fingerprint entropy
- How a site can compare HTTP headers with JavaScript APIs
- How to choose privacy signals for each profile and keep them consistent
- The specific checks to run to verify your configuration
Any browser will do for the checks below. The concepts apply to every tool, including multi-account browsers like JustBrowser, though implementation quality varies a lot between vendors.
Prerequisites
- A browser, or a multi-account browser, with configurable privacy settings
- Basic understanding of HTTP headers and browser APIs
- Access to browser developer tools (F12)
- 20 minutes
If you're new to fingerprinting fundamentals, the WebGL fingerprinting deep-dive provides useful background on how fingerprinting libraries cross-reference multiple signals. For teams evaluating tools, 5 things everyone gets wrong about multi-account browsers is a useful primer.
Step 1: Understanding What These Signals Actually Are
Let's get specific. There are two separate privacy signal systems, and they work differently.
Do Not Track (DNT) is an HTTP header. When enabled, your browser sends DNT: 1 with every request. Websites were asked to honour it, and few major sites do. But that's beside the point for fingerprinting.
You can check your current DNT status in JavaScript:
navigator.doNotTrack
// Returns "1", "0", or null
Global Privacy Control (GPC) is newer, and it has legal weight: California's CCPA regulations require covered businesses to treat it as a valid request to opt out of the sale or sharing of personal data. It sends a Sec-GPC: 1 header and exposes a JavaScript API:
navigator.globalPrivacyControl
// Returns true, false, or undefined
Here's where it gets interesting for fingerprinting. These signals are rare. Most browsers have both switched off, and only a small minority of users enable either one.
If you're in that minority, you've added entropy to your fingerprint. Not because the signal itself is tracked, but because it puts you in a smaller bucket than the default.
Step 2: The Cross-Check Problem
This is where privacy settings bolted on from outside the browser can go wrong.
A site doesn't have to stop at reading navigator.doNotTrack. It can compare it with other sources:
- HTTP header vs JavaScript API: Does the
DNTheader value match whatnavigator.doNotTrackreturns? - Sec-GPC header vs navigator.globalPrivacyControl: Same check for GPC.
- Consistency across contexts: Does a Web Worker report the same GPC value as the main thread? (
doNotTrackisn't exposed to workers, so only GPC can be compared there.) - Session stability: Do these values stay constant across page loads?
An extension that injects JavaScript to override navigator.doNotTrack or navigator.globalPrivacyControl changes only what page scripts read. The request headers are a separate mechanism: the extension has to rewrite them too, through its own header rules, and the two have to stay in step. When only one layer changes, you end up with:
HTTP Request: DNT: 0
JavaScript: navigator.doNotTrack === "1"
That is a contradiction any site can see, and for a privacy user it means the setting is only half-applied.
A setting implemented inside the browser engine sends the header and returns the property from the same value, so there is one thing to get right rather than two. The same architectural difference matters for TLS/JA4 fingerprinting, which an extension cannot change at all.
Step 3: Checking Your Current Configuration
Open a profile and run these checks in the browser console (F12 → Console):
// Check DNT
console.log("DNT API:", navigator.doNotTrack);
// Check GPC
console.log("GPC API:", navigator.globalPrivacyControl);
// Check GPC from a Worker (doNotTrack isn't exposed to workers)
const blob = new Blob([`
postMessage({
gpc: navigator.globalPrivacyControl
});
`], {type: 'application/javascript'});
const worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = (e) => console.log("Worker values:", e.data);
Expected output for a profile with both signals switched off:
DNT API: null (or "0" or "unspecified")
GPC API: undefined (or false)
Worker values: {gpc: undefined}
If the main thread and worker GPC values differ, the setting comes from JavaScript injection that doesn't reach workers, so it is only partly applied.
Now verify the HTTP headers. Open Network tab, reload the page, click any request, and check the request headers:
- Look for the
DNT:header — absent (or0) when DNT is off,DNT: 1when it's on - Look for the
Sec-GPC:header — absent when GPC is off,Sec-GPC: 1when it's on
If the headers don't match the JavaScript API values, the setting is half-applied. A tool might send DNT:1 in headers while the JavaScript returns null because it only changes one layer, and then the server and the page's scripts get different answers.
Step 4: Choosing Privacy Signals for Each Profile
Start from what the profile is for.
Your own private browsing:
- GPC: on, if you want sites to treat your visits as an opt-out of sale or sharing
- DNT: optional; few sites honour it, and it mostly adds entropy
- Accept the trade-off: either signal puts you in a smaller group than the default
QA on your own site:
- One profile with GPC off, one with GPC on
- Check that the site honours the opt-out when
Sec-GPC: 1arrives, for example that your consent banner and any sale-or-sharing flags reflect it - Keep everything else about the two profiles the same, so the GPC setting is the only difference you are testing
Client or team profiles for everyday work:
- Leave the signals at whatever the browser's own settings are, unless the person using the profile has chosen otherwise
What to avoid, whatever the purpose:
- A setting that reaches only one layer: the header without the property, or the property without the header
- Values that change between sessions, which usually means broken state management
Whichever tool you use, check whether its setting affects the HTTP headers or only JavaScript. If the documentation doesn't say, test it yourself with the methods above. Some tools offer a GPC option that only sets the JavaScript API, which leaves the header, the part with legal weight, unsent.
Step 5: Reading the Results in Public Tools
Once configured, check what public fingerprinting pages report for your profile. You are confirming your own settings, not chasing a score.
CreepJS shows privacy API values in the Navigator section. Expand it and look for:
doNotTrackglobalPrivacyControl
Values should match your configuration. If CreepJS shows a value you didn't set, something is overriding it.
BrowserLeaks (browserleaks.com/javascript) has a dedicated section for privacy APIs. Run it and verify the output matches your settings.
The cross-check workflow:
- Configure profile with specific privacy settings
- Run CreepJS — verify JavaScript API values
- Check Network tab — verify HTTP headers match
- Run Web Worker test — verify cross-context consistency
- Run BrowserLeaks — compare its output with your settings
If any step disagrees, fix the profile before you rely on it.
Common Errors and Fixes
Error: navigator.globalPrivacyControl returns undefined but Sec-GPC header is sent
Cause: Your browser is sending the GPC header (possibly from a default or cached setting) but the JavaScript API isn't configured to match.
Fix: This usually indicates stale profile state. Create a fresh profile or check whether your tool has separate header and API controls. They need to match.
Error: GPC value differs between main thread and Web Worker
Cause: The setting comes from JavaScript injection that only affects the main thread.
Fix: Use a browser that applies the setting natively; configuration inside an injected script can't reach every context. See how extension-based and native multi-account browsers differ for the full breakdown.
Error: Privacy values change between page loads
Cause: The tool is randomizing these values, which is worse than a stable setting.
Fix: Check for randomization settings and disable them for privacy signals. These should be stable per-profile, not per-session.
Error: Privacy signals are right but other values disagree
Cause: Privacy signals are one of dozens of values a browser exposes. The mismatch may be elsewhere.
Fix: Check the other values too: canvas and audio fingerprinting, TLS fingerprinting, and whether the timezone agrees with the network location.
Next Steps
Privacy signals are a small piece of the fingerprinting picture, but they are the kind of detail that separates a consistent profile from a contradictory one. The irony, privacy tools making you more distinctive, is a long-running debate in the privacy community.
Once your privacy signals are configured correctly:
- Review your other fingerprint signals — the TLS fingerprinting explainer covers the network layer
- Look at HTTP/2 too — privacy headers are sent over HTTP/2, which has its own fingerprint
- Re-check after browser updates — defaults and APIs change
For teams running ad verification, pair your setup with ClickzProtect for traffic quality monitoring. Profiles that behave predictably are the ones you've checked at every layer. If you're automating profile management, the Playwright/Puppeteer REST API integration works well for scheduled checks.
One more thing: fingerprinting tools and browser defaults change over time. Build a periodic check into your workflow. A monthly spot-check takes five minutes and catches drift early.
Frequently Asked Questions
Does enabling Do Not Track make me more trackable?
In a narrow sense, yes. Only a small minority of browsers send DNT:1, so turning it on puts you in a smaller group than the default. The signal was designed for privacy, but because so few people use it, it adds entropy to a fingerprint. That is a trade-off, and some people accept it on purpose.
What is navigator.globalPrivacyControl and why does it matter?
navigator.globalPrivacyControl is a JavaScript property that returns true when a user has asked sites not to sell or share their data, a request that laws such as CCPA recognise. Like DNT, its rarity makes it a fingerprint signal. If a browser's GPC state does not match its Sec-GPC header, or changes between sessions, the inconsistency is easy to spot.
Why can extension-based privacy settings end up inconsistent?
An extension that injects a script to override navigator.globalPrivacyControl only changes what page scripts read. The Sec-GPC header is set by a separate mechanism, and the extension has to rewrite request headers as well for the two to agree. A site can compare them. A setting applied inside the browser engine produces both from one value.
What privacy signal configuration should I use for a profile?
Decide what each profile is for. For your own browsing, turn GPC on if you want sites to treat it as an opt-out of sale or sharing; DNT is rarely honoured and mostly adds entropy. For QA on your own site, keep one profile with GPC on and one with it off and check that the site responds correctly. Either way, make sure the HTTP header and the JavaScript API agree and the value stays the same between sessions.
Try JustBrowser
A multi-account browser for teams, built on a custom Chromium engine with native C++ integration, not an extension. Each profile is its own browser with its own cookies, storage, proxy and 40+ identity parameters, and teammates can share profiles. REST API and CDP for Playwright, Puppeteer, Selenium. Windows x64 and macOS (Apple Silicon). $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.