Fingerprints
What a VeilBrowser fingerprint actually is, how one gets selected, and which signals you control.
A fingerprint in VeilBrowser is not a list of values you fill in. It is a curated browser build — an encrypted profile bundle shipped with the platform — that presents a coherent device to every site it visits. You choose which bundle a profile uses by describing the device you want; the platform matches your criteria against the catalog and pins the result.
Understanding this distinction is the difference between using the product correctly and expecting knobs that do not exist.
Why bundles instead of patches
Most antidetect tooling injects JavaScript into a stock browser to overwrite
navigator, patch toDataURL, and shim WebGL parameters. That approach leaks in three
predictable ways:
- Detectable overrides. A patched function has a different
toString(), a different prototype chain, and different timing than a native one. - Internal inconsistency. Patching the reported GPU string does not change what the rasterizer actually produces, so the claimed device and the rendered output disagree.
- Drift. Values recomputed per launch shift over time, which turns one identity into a cluster of near-identical ones — a strong correlation signal on its own.
VeilBrowser ships the signals in the browser binary and its profile bundle instead. The canvas output, WebGL parameters, audio hash and font set are properties of the build, not of a script running inside it. That is what makes a profile present the same device on every launch.
Consistency is the product
The goal is not a random fingerprint per launch — it is a stable, believable one per profile. One profile equals one device, for as long as that profile exists.
Three tiers of control
This is the part of the product most often misread. Fingerprint signals are not one undifferentiated pile of settings. They fall into three tiers, and each tier is owned by something different.
You choose
Selection criteria and per-profile settings: os, browserVersion, osVersion, osArch, renderer, model, userAgent, screen and proxy.
Derived from the network route
Timezone, locale, Accept-Language and geolocation, resolved from the proxy's exit IP at launch.
Fixed by the profile
Canvas, WebGL, WebGPU, audio, fonts, media devices, speech voices, client hints and device metrics. Coherent by construction.
You do not assemble a device signal by signal. You pick a device, attach a route, and the platform makes the two agree. Roughly 45 distinct fingerprint signals are controlled across those tiers.
How a bundle gets selected
When you create a profile you supply selection criteria. The worker resolves them against its local catalog of bundles and pins the match, so subsequent launches of the same profile reuse the same bundle.
| Criterion | Effect |
|---|---|
os | win, mac, linux or android — drives platform, fonts and device metrics |
browserVersion | Chrome major, 135–150 |
osVersion | Narrows to a specific OS release, e.g. 11 or 14 |
osArch | x86 or arm |
renderer | Partial match against the unmasked WebGL renderer string |
model | Device model, Android bundles only |
Workers expose the catalog directly: GET /profiles lists available bundles with their
gpuName, unmaskedRenderer, windowsVersion and arch, and GET /profiles/versions
lists the browser majors installed on that host. Use these to discover what your fleet
can actually produce before you write criteria that match nothing.
Android is a first-class target, not a preview. Android bundles run end-to-end on
workers, GET /profiles/android-models lists the device models available for a given
browser version, and the bundles carry touch simulation, mobile device metrics,
orientation and a mobile keyboard viewport. Both the dashboard profile form and the API
accept android.
What you choose
These are settable per profile or per launch, and they take effect:
Prop
Type
renderer is worth calling out: it is a selection criterion matched against the
unmasked WebGL renderer string of the catalog, not a free-text override. You are asking
for a bundle whose GPU already reports that string, which is why the reported GPU and
the pixels it produces continue to agree.
What the profile fixes
Everything below comes from the bundle rather than from configuration, which is what makes it coherent. These values are identical on every launch of the profile, and identical whether the session runs headless or in a visible window.
- Canvas, audio, client-rect and text-rect rendering. Deterministic per-profile noise, so readings are stable over time instead of drifting into a cluster of near-identical identities.
- WebGL and WebGPU. Vendor, renderer, parameters and the actual rendered output all agree with each other.
- Fonts. The build carries embedded platform font engines for Windows, macOS, Linux and Android, so a macOS profile enumerates macOS fonts while running on a Linux worker. The host's own fonts never leak during cross-OS operation.
- Media devices and speech synthesis voices. Enumerated consistently with the platform the profile claims.
- Client Hints. DPR, device-memory and UA-CH headers match their JavaScript counterparts, so header and script readings cannot be played off against each other.
- Hardware concurrency and device metrics. Fixed to the device the bundle describes.
- Widevine and WebAuthn. Persistent-license handling and uniform WebAuthn capabilities, both common sources of accidental uniqueness.
Noise is deterministic and profile-backed, not a dial. There is no per-profile noise level to tune, and that is deliberate: a tunable noise slider produces readings that differ from launch to launch, which is the drift problem restated.
What the network route derives
Timezone, locale, Accept-Language and geolocation are resolved from the exit IP of the
profile's proxy when the browser launches. Nothing needs to be configured for this, and
nothing needs to be kept in sync by hand — assign a proxy and the region-dependent
signals follow it. A profile with no proxy falls back to the host's own network
position. See Network and leak prevention for the mechanism
and for how WebRTC, QUIC and DNS are handled.
Overriding an automatic value
The derived values exist because they are right by default, and pinning one by hand is
the usual way a profile ends up disagreeing with its own route. For the cases where an
advanced caller genuinely needs a specific value, the launch body accepts
browser.args, an array of additional browser command-line arguments applied at launch:
{
"profileId": "<profile-uuid>",
"browser": {
"args": ["--window-size=1280,800"]
}
}An argument passed here takes precedence over the automatic value for the signal it addresses, which is what makes explicit pinning possible. Treat it as an escape hatch for a specific, justified case rather than part of a normal launch path, and re-verify the profile afterwards.
Operating an identity
- Keep
osandbrowserVersionstable for the life of an identity. Changing either re-selects the bundle, which is equivalent to the user buying a new computer. - Do not rotate fingerprints on a schedule. A device that changes weekly is more anomalous than one that never changes.
- Give each profile its own proxy. Two identities sharing an exit IP correlate through the network even when their devices are unrelated.
- Verify before you scale. Launch one profile, check it against a fingerprinting test page, and confirm the device story holds together before provisioning hundreds.
Next steps
- Profiles — create profiles with these criteria.
- Proxies — the network half of an identity.
- Network and leak prevention — alignment, WebRTC, QUIC and DNS.
- What is browser fingerprinting? — background on the signals themselves.