A Chromium build that lets you control what your browser reports about your machine (GPU, canvas, audio, fonts, screen, hardware, timezone) instead of providing a stable, unique fingerprint to every site you visit.
Spoofing happens inside the browser, not through injected JavaScript, so there's no Object.defineProperty wrapper for a page to detect. Identities are either random per launch or reproducible from an integer seed.
Windows only. Closed source, distributed as a binary. See Trust & verification before installing.
- Download
- Trust & verification
- Install
- What this does and doesn't do
- Privacy defaults
- Fingerprint identity
- Command-line switches
- Automation: custom CDP methods
- Intended use
- License
Grab the latest build from the Releases page:
| File | Description |
|---|---|
AntiFingerprintChromium-Setup-<version>.exe |
Installer with Windows integration |
AntiFingerprintChromium-<version>-portable.zip |
Portable, no installation |
The source is closed, and I don't intend to open it. I don't have any experience with open source so I'd rather keep it closed for now. If someone would like to help me safely open-source this project just email me [support@nodriver.app].
Use Mullvad Browser, LibreWolf, or Brave instead if you need open source.
What you can check independently today:
- Run the browser against CreepJS, BrowserScan, or FingerprintJS with spoofing on and off, and compare.
- Monitor its network traffic with Wireshark or Fiddler to confirm the telemetry claims.
- Download
AntiFingerprintChromium-Setup-<version>.exeand run it. - Choose an install location (or accept the default) and all-users vs. just-me.
- It installs the browser, adds Start Menu / desktop shortcuts, and registers it with Windows as a web browser.
- To make it your default: tick "Choose … as your default browser" on the last wizard page, or later open Settings → Apps → Default apps, find Anti-Fingerprint Chromium, and set it for
http,https, and.html.
Uninstall from Settings → Apps like any normal program.
Windows SmartScreen may warn on first run, since the build isn't code-signed with an EV certificate.
- Extract the zip anywhere and run
chrome.exe, no installation required. - (Optional) To make the portable copy selectable as your default browser, run the included
register-browser.ps1from the extracted folder in an elevated PowerShell (or add-PerUserto register for just your account, no admin needed), then set it in Settings → Default apps. Undo any time withregister-browser.ps1 -Unregister.
This browser takes a different approach from Tor Browser and Mullvad Browser.
Tor and Mullvad aim to make every user look identical: one large anonymity set where you're indistinguishable from everyone else running the same build. This browser instead gives you a plausible and distinct identity. What makes this approach better than Tor and Mullvad is the increased trust sites have in your browser session. Tor and Mullvad experience captchas and blocks on high-security sites because of their aggressive privacy settings. This browser does not have this issue while still providing good protection from cross-site tracking.
What it does not do:
- It is not a complete anonymity tool. Your IP address is untouched unless you supply your own proxy or VPN.
- It does not defeat login-based tracking. If you sign into an account, the fingerprint is irrelevant.
- It does not guarantee evasion of any specific anti-bot system.
The always-on privacy preset turns off Google Safe Browsing, which is what normally warns you about phishing pages and malware downloads. This removes a layer of protection for everyday browsing. I turned it off to speed up page load times and decrease network usage.
A telemetry-off + privacy preset is applied automatically on every launch, with no flags or shortcuts needed. It turns off pings, background networking, usage/crash reporting, and keeps Manifest V2 extension support enabled, so uBlock Origin works as it did pre-Chrome-127.
To launch with stock Chromium behavior instead, add --use-chromium-defaults.
Fingerprint spoofing is off by default. Turn it on at chrome://browser-settings:
- Off: no spoofing (standard Chromium fingerprint).
- Auto: a fresh random identity each launch.
- Fixed seed: a stable identity derived from a number you choose (the same number reproduces the same identity across launches).
Your choice is saved and applied on every launch, no matter how the browser is started, whether by a clicked link, a shortcut, or as your default browser. Restart the browser after changing it. (This is equivalent to the --fingerprint-seed switch below; passing --fingerprint-seed on the command line overrides the saved value for that one launch.)
Which mode you want depends on the goal. Auto breaks correlation between sessions and suits general browsing. Fixed seed keeps you consistent to sites that would find a machine whose hardware changes daily more suspicious than one that never changes at all.
Most people won't need these: the privacy defaults are automatic and the fingerprint identity is set at chrome://browser-settings. The switches below are for finer control. Pass one by adding it to the Target field of your browser shortcut. It then applies to launches from that shortcut only, while the privacy defaults and the saved fingerprint seed apply to every launch however the browser is started.
Notation in the Modes column: explicit means a literal value; auto randomizes once per launch; seed:N derives deterministically from the integer N (same N across two launches produces identical values).
The Master column indicates whether the switch activates implicitly when --fingerprint-seed=auto|<int> is set with no per-switch override. Switches without a ✓ require explicit opt-in.
Master / preset switches
| Switch | Modes | Description |
|---|---|---|
--fingerprint-seed=auto|<int> |
auto, integer | Master seed driving every "Master ✓" switch below. auto allocates one random seed per launch; <int> is reproducible across launches. Usually set persistently via chrome://browser-settings rather than here; a command-line value overrides the saved one for that launch. |
--use-chromium-defaults |
presence-only | Opt out of the always-on browser-defaults preset for this launch, running with stock Chromium behavior. |
--browser-defaults |
presence-only | No-op, kept for backward compatibility: the preset it used to enable is now applied by default on every launch (opt out with --use-chromium-defaults). |
Fingerprint switches: activated by --fingerprint-seed
These derive from the active profile (a coherent identity picked from an embedded pool) or from pure seed math. All accept per-switch auto or seed:N for fine-grained control.
| Switch | Modes | Master | What it spoofs |
|---|---|---|---|
--fingerprint-gpu-vendor=<string> |
auto, seed:N, explicit | ✓ | gl.getParameter(UNMASKED_VENDOR_WEBGL) |
--fingerprint-gpu-renderer=<string> |
auto, seed:N, explicit | ✓ | gl.getParameter(UNMASKED_RENDERER_WEBGL) |
--fingerprint-webgpu-vendor=<string> |
auto, seed:N, explicit | ✓ | (await navigator.gpu.requestAdapter()).info.vendor. Derived from the WebGL renderer via Dawn's vendor/device-ID database. |
--fingerprint-webgpu-architecture=<string> |
auto, seed:N, explicit | ✓ | …info.architecture. Same derivation as vendor. |
--fingerprint-hardware-concurrency=<int> |
auto, seed:N, explicit | ✓ | navigator.hardwareConcurrency |
--fingerprint-device-memory=<int> |
auto, seed:N, explicit | ✓ | navigator.deviceMemory. Snapped to {2,4,8,16,32}. |
--fingerprint-audio-context=<sr,bl,ol,mcc> |
auto, seed:N, explicit | ✓ | new AudioContext() properties: sampleRate, baseLatency, outputLatency, destination.maxChannelCount. |
--fingerprint-battery=<charging,level,ct,dt> |
auto, seed:N, explicit | ✓ | navigator.getBattery() fields. Derive branches on the active profile's device type (desktop = always plugged; laptop = sampled). |
--fingerprint-connection=<eff,dl,rtt,sd,type> |
auto, seed:N, explicit | ✓ | navigator.connection properties + HTTP Client Hint headers (Downlink, RTT, ECT). Format: effectiveType,downlinkMbps,rttMs,saveData,connectionType. |
--fingerprint-brand=<string> |
auto, seed:N, explicit | ✓ | navigator.userAgentData.brands[].brand. Auto default: "Chrome". |
--fingerprint-brand-version=<int> |
auto, seed:N, explicit | ✓ | UA Chrome/<N>.0.0.0 major segment. Auto default: real Chromium major version. |
--fingerprint-brand-version-long=<full> |
auto, seed:N, explicit | ✓ | navigator.userAgentData.getHighEntropyValues(['fullVersionList']). Auto default: real PRODUCT_VERSION. |
--canvas-curve-noise=<float> |
auto, seed:N, explicit | ✓ | Sub-pixel perturbation on Canvas 2D curve verbs (bezier, arc, quadratic). Range [0.003, 0.015]. |
--canvas-blur-noise=<float> |
auto, seed:N, explicit | ✓ | Multiplicative perturbation on Gaussian blur sigmas (shadowBlur, ctx.filter='blur(...)'). Range [0.005, 0.03]. |
--canvas-gradient-noise=<float> |
auto, seed:N, explicit | ✓ | Additive perturbation on Canvas 2D gradient color-stop offsets. Range [0.003, 0.02]. |
--webgl-shader-noise=<float> |
auto, seed:N, explicit | ✓ | Sub-LSB perturbation on WebGL fragment-shader natural-variance constructs. Range [1e-3, 3e-3]. |
--browser-font-bundle=<seed-string> |
auto, seed:N, explicit | ✓ | Selects an on-disk font bundle from browser_fonts/; affects document.fonts enumeration and text metric widths. |
Fingerprint switches: explicit opt-in only
These exist but are not activated by --fingerprint-seed. Pass them with =auto, =seed:N, or a literal value to use them.
| Switch | Modes | What it spoofs |
|---|---|---|
--fingerprint-screen=<w,h,aw,ah,at,cd[,dpr]> |
auto, seed:N, explicit | screen.width/height/availWidth/availHeight/availTop/colorDepth plus window.devicePixelRatio (optional 7th field). |
--fingerprint-screen-position=<x,y> |
auto, seed:N, explicit | window.screenX/screenY (headless mode only). |
--fingerprint-media-features=<scheme,motion,contrast,colors,gamut> |
auto, seed:N, explicit | CSS matchMedia('(prefers-color-scheme: …)'), (color-gamut: …), (prefers-reduced-motion: …), (prefers-contrast: …), (forced-colors: …). |
--fingerprint-media=<mics,webcams,speakers> |
auto, seed:N, explicit | navigator.mediaDevices.enumerateDevices() per-kind counts. Pre-permission state collapses any non-zero count to one entry per kind. |
--fingerprint-history-length=<int> |
auto, seed:N, explicit | history.length. Derive range [1, 5]. |
--audio-noise=<float> |
auto, seed:N, explicit | Per-sample Gaussian noise amplitude for OfflineAudioContext rendering. Range [1e-7, 1e-6]. |
--fingerprint-text-rendering=gamma:<float>,contrast:<float> |
explicit only | Skia text_gamma / text_contrast overrides (Windows). Retained for experimentation; no auto/seed:N support. |
Proxy-derived and utility switches
These don't fit the seed model. Pass a literal value or omit. Set them to match whatever proxy or VPN exit you're using. A fingerprint that claims America/New_York while your traffic exits in Frankfurt is more identifying than no spoofing at all.
| Switch | Modes | Effect |
|---|---|---|
--fingerprint-timezone=<IANA> |
explicit only | ICU timezone override (e.g. America/New_York). |
--fingerprint-geolocation=<lat,lon,acc> |
explicit only | navigator.geolocation.getCurrentPosition() override. |
--webrtc-ip4=<ip> |
explicit only | WebRTC ICE-candidate IPv4 + SDP override. |
--webrtc-ip6=<ip> |
explicit only | WebRTC ICE-candidate IPv6 + SDP override. |
--fingerprint-accept-language=<BCP-47> |
explicit only | Forces the HTTP Accept-Language header (no effect when a font bundle is active, since the bundle owns the header). |
| Switch | Modes | Effect |
|---|---|---|
--auto-decline-webauthn |
presence-only | Auto-rejects WebAuthn/passkey prompts with NOT_ALLOWED_ERROR after a randomized 1–5 s delay. Intended for automated runs where no authenticator is attached and a hanging prompt would stall the session. |
--show-cdp-cursor |
presence-only | Renders the CDP-driven cursor overlay when DevTools is connected. Useful for visual automation debugging. |
Three commands extend the standard Chrome DevTools Protocol, aimed at browser automation and UI testing where per-event round-trip latency is the bottleneck. They're listed in the protocol JSON at http://127.0.0.1:<port>/json/protocol and can be invoked through any CDP client (chrome-devtools-frontend, Puppeteer / Playwright via CDPSession.send(...), nodriver / Selenium CDP wrappers, raw WebSocket, etc.).
Walks a pre-computed cursor trajectory in a single CDP call instead of N per-segment Input.dispatchMouseEvent round-trips. Chromium dispatches the events internally at a fixed 1000 / pollingRateHz ms interval, so polling rates of 500–1000 Hz become achievable (per-segment CDP round-trip latency normally caps effective rates around 50–100 Hz).
Parameters:
path: array of{ x: number, y: number }positions in CSS pixels.pollingRateHz: integer (e.g.125,500,1000).
Returns: { x, y }, the final cursor position.
{
"method": "Input.dispatchMousePath",
"params": {
"path": [{"x": 100, "y": 100}, {"x": 105, "y": 102}, {"x": 110, "y": 105}],
"pollingRateHz": 500
}
}Types a string with variable inter-key timing, optional pauses, and optional typo + correction sequences. The full schedule runs inside the browser process, so per-key round-trip latency is gone: one CDP call per type() operation regardless of length.
Parameters:
text: string to type.\nmaps to Enter.speedMultiplier: number > 0, default1.0. Values > 1 type faster.keystrokeDelayMin/keystrokeDelayMax: integer ms, default60/260. Inter-keystroke delay range (before speed scaling).keyHoldMin/keyHoldMax: integer ms. keyDown to keyUp hold duration range.contextAware: bool, defaulttrue. Applies bigram-frequency and post-punctuation timing adjustments.thinkingPauseProbability: number 0–1, default0. Chance of a longer pause before a character. Pair withthinkingPauseMin/thinkingPauseMax(integer ms).typoProbability: number 0–1, default0. Chance of injecting a typo + backspace + correction. Realistic values:0.01–0.03.seed: number, optional. RNG seed for a deterministic schedule (same params + text + seed produces an identical event sequence), which makes test runs reproducible.
Returns: nothing meaningful. The call blocks until the full schedule has run.
{
"method": "Input.dispatchKeystrokes",
"params": {
"text": "hello world",
"speedMultiplier": 1.2,
"typoProbability": 0.02,
"thinkingPauseProbability": 0.05
}
}Returns the shadow root of a host element regardless of closed vs open mode. Standard DOM.describeNode (even with pierce: true) skips closed roots; this command exposes them. Useful for testing pages that encapsulate components behind closed roots, which are otherwise unreachable from an automation harness.
The access is not observable from page JavaScript.
Parameters:
nodeId: integer. The host element's CDP node ID.
Returns: { shadowRoot: Node | null }, the shadow root as a standard CDP Node, or null if the element has no shadow root.
{
"method": "DOM.getShadowRoot",
"params": { "nodeId": 42 }
}Once you have the shadow root's nodeId, the standard DOM.querySelector, DOM.describeNode, DOM.resolveNode, etc. all work against it.
This is built for people who want control over what their browser discloses: privacy-conscious browsing, security research, testing how your own sites respond to varied client configurations, and automation against systems you own or are authorized to access.
It isn't built for evading bans, operating fake accounts at scale, or circumventing access controls on services you don't own. Those uses tend to violate the terms of service of whatever you're pointing it at, and in some jurisdictions bypassing technical access restrictions carries legal exposure beyond a terms violation. What you do with it is on you.
Bug reports and feature requests are welcome via Issues. Fingerprint-detection reports are especially useful: if a site distinguishes this browser from stock Chrome, open an issue with the site and what gave it away.
Built on Chromium. The Safe Browsing removal draws on the ungoogled-chromium project's work.
See LICENSE.txt. Free for personal and commercial use, with no warranty.
Chromium itself is BSD-3-Clause and bundles components under other licenses; those obligations carry over to this derivative. Third-party notices are listed at chrome://credits within the running browser.