DuckDuckGo's browser adds canvas noise and pins some hardware values, but it won't make you look like everyone else. What the code changes, and what it leaves.
People who open a fingerprint test in the DuckDuckGo browser usually want one answer: is my fingerprint protected? The short version is that the browser reduces some fingerprinting signals, but it does not try to make every user look identical, the way Tor Browser does. Its protections are a mix of two strategies, and both are visible in its open-source code.
Key Takeaways
- DuckDuckGo's browser protections are injected as page scripts from the open-source content-scope-scripts repository, and switched on or off by a remotely served configuration in privacy-configuration.
- Strategy one is noise: canvas readbacks are altered with a value derived from the site and the browsing session, so the hash differs per site and per session.
- Strategy two is fixed values:
hardwareConcurrency,deviceMemory, parts ofscreenand the Battery API return the same answer for everyone. - Nothing here makes you uniform. The browser engine, User-Agent, WebGL renderer and fonts are not touched by the features covered in this article.
- Behaviour can differ by platform and by site, because the configuration has per-feature switches and a list of per-domain exceptions.
Two Strategies, Not One
Fingerprint defences generally pick between two ideas. Uniformity makes many users return the same values, so the value stops identifying anyone; Firefox and Tor's resistFingerprinting works this way. Noise perturbs the output so a tracker cannot hold a stable value; the trade-offs of noise are covered here.
DuckDuckGo uses both, feature by feature. Noise is applied where the raw value is high-entropy and naturally varies (canvas pixels). Fixed values are used where a single number is cheap to normalise (core count, memory, colour depth). It is a pragmatic mix aimed at reducing cross-site tracking without breaking too many sites, not a promise of anonymity.
What the Code Changes
Each feature is a small file under injected/src/features/ in content-scope-scripts, paired with a JSON file under features/ in privacy-configuration.
Canvas (fingerprinting-canvas.js). The script wraps getImageData, toDataURL and toBlob. It only treats a canvas as suspicious after drawing calls such as fillText, arc or gradient creation. When such a canvas is read back, the script copies it to an off-screen canvas and modifies the pixels there, using a key built from the session key and the site's domain (an HMAC computed over a checksum of the pixels). The change is tiny: it flips the lowest bit of one colour channel on a subset of pixels, skipping pixels identical to their neighbours. The hash changes; the picture looks the same. Because the key is stable inside a session, reading the same canvas twice returns the same bytes, which avoids the double-read tell discussed in our canvas noise article. The shared fingerprinting-canvas.json also sets webGl to "enabled", which makes WebGL calls such as shaderSource and drawArrays mark a canvas as suspicious too, so a WebGL-rendered image exported with toDataURL gets the same treatment. That covers rendered pixels only, not the GPU vendor and renderer strings.
Audio (fingerprinting-audio.js). The code adds a minuscule offset to samples returned by getChannelData, copyFromChannel and the analyser methods, again keyed by session and domain. But in the shared fingerprinting-audio.json the feature's state is "disabled" at the time of writing, so do not assume audio noise is active. See audio fingerprinting for what that signal exposes.
Hardware (fingerprinting-hardware.js). navigator.keyboard is made undefined, and hardwareConcurrency and deviceMemory return configured numbers. The shared config currently sets them to 4 and 8; re-read the JSON before relying on this, since it is remotely updated. For background on these values, see hardware concurrency and device memory.
Screen (fingerprinting-screen-size.js). availWidth and availHeight are forced to the full screen width and height, availTop and availLeft to 0, and colorDepth and pixelDepth to 24, which hides the size of taskbars and docks. It also normalises screenX/screenY for multi-monitor setups. Width and height themselves still come through; see screen resolution fingerprinting.
Battery and storage. fingerprinting-battery.js reports a fully charged, charging battery and silences its events. fingerprinting-temporary-storage.js caps the quota that webkitTemporaryStorage.queryUsageAndQuota reports at 4 GB, which blunts disk-size inference.
Honest Limits
Remote configuration. Every feature is gated by the configuration. The canvas script even returns early unless an additionalEnabledCheck setting is enabled, and the audio feature is disabled in the shared file. Which protections run therefore depends on the platform build and the current configuration, not only on the code.
Per-site exceptions. The configs list domains where a protection is switched off because it broke the site. fingerprinting-hardware.json alone lists 15, with reasons such as "Clicking 'add to cart' causes a spinner to load briefly, and the item doesn't get added to the cart" for gamestop.com. On those sites, that protection is off.
Untouched surfaces. None of the files above change the User-Agent, WebGL renderer strings or the installed font list, and the browser is built on the platform's own web engine. Those signals still contribute to a fingerprint. The repository has no canvas-style feature for them among the files reviewed here, but we have not audited the whole project, so treat this as "not covered by these features" rather than "never protected".
Fixed values can stand out. Reporting 4 cores on a 16-core desktop is a value a script can cross-check against other signals, and such a mismatch can itself be a signal; see fingerprint hardware mismatch.
How It Compares
| Browser | Main idea | Example |
|---|---|---|
| DuckDuckGo | Noise on canvas, fixed values for a few attributes | Canvas bit-flips, fixed core count |
| Brave | Per-session, per-site "farbling" across many APIs | See Brave's privacy updates |
| Firefox / Tor | Uniformity (resistFingerprinting) | Rounded timers, standard window size |
This is a comparison of strategy, not a scoring of which is "best". For the underlying techniques, start with browser fingerprinting explained. One more clarification: DuckDuckGo the search engine is a separate product; its privacy promise concerns search logging, not browser fingerprinting.
Test It Yourself
Open our fingerprint check in the DuckDuckGo browser, note the canvas hash, then reload and visit it again in a new session. A noised canvas hash should change between sessions while values such as the core count and device memory stay fixed. We do not label results as "DuckDuckGo protection detected": the tool shows raw values, and you draw the conclusion.
Frequently Asked Questions
Does the DuckDuckGo browser block fingerprinting completely?
No. It alters some readbacks and normalises some attributes, but other signals such as the engine, User-Agent and WebGL strings are outside the features described here.
Will my canvas fingerprint be the same on every site?
Not if the canvas protection is active: the noise key includes the site's domain, so the same drawing gives different hashes on different sites and in different sessions.
Is DuckDuckGo's protection the same on every device?
Not necessarily. Features are enabled through remotely served, per-platform configuration, so behaviour can differ by operating system and app version.

