Dual Login
Technical

Browser Fingerprinting and Web Scraping Detection Explained

Dual Login Team·2026-08-08·24 min read

Browser Fingerprinting and Web Scraping Detection Explained

How sites build a stable device identity, why proxies alone never fix a blocked scraper, and how to design collection that survives modern bot management.

Browser Fingerprinting and Web Scraping Detection Explained

Browser fingerprinting and web scraping detection signals layered across proxy, TLS and JavaScript

A collection job that had run clean for six months stopped working on a Tuesday. Nothing had changed on our side: same code, same proxy pool, same schedule. First a handful of 403s, then challenge pages on a third of requests, then challenge pages on everything. The reflex in that moment is always the same — rotate harder, buy more IPs, add a sleep. That reflex is usually wrong, and acting on it is how teams burn a five-figure residential proxy budget solving a problem that lives somewhere else entirely.

Browser fingerprinting and web scraping detection are two ends of the same conversation. Fingerprinting is how a site builds a stable identifier for the machine on the other end of the socket. Detection is what the site does with that identifier once it concludes the machine is not a person. If you only understand the second half, you spend your life chasing symptoms. If you understand the first half, you can usually predict — before you deploy anything — which of your requests are going to survive the week.

This is a working guide, not a bypass manual. I am not going to walk through defeating any named vendor, because that content ages in weeks and mostly helps people doing things they shouldn't. What follows is the durable part: what the signals are, how they combine into a score, why the classic scraper stack leaks, and how to architect collection so that the identity you present is boring enough to be ignored.

Detection is a score, not a gate

The mental model most people start with is a bouncer at a door: you either look right or you don't. That is not how modern bot management works. Every request arrives at a scoring function that reads dozens of independent signals, weights them, compares the result against a threshold, and picks an action. Allow. Challenge. Serve degraded content. Block. Silently poison the response with plausible but wrong data — that last one is more common than people realise and it is the reason you should always validate scraped data against a known-good sample.

Two consequences fall out of the scoring model, and both are important.

The first is that a block is the end of a story that started several requests earlier. By the time you see a 403, your identity has usually been accumulating risk for a while: an odd header order here, a canvas hash that matches four hundred other sessions there, a request cadence with no human variance. The 403 is the moment the running total crossed a line. Debugging the request that failed is often less useful than looking at the twenty that preceded it.

The second is that no single signal has to be conclusive. A datacentre IP is not proof of automation — plenty of real users sit behind corporate egress. A missing plugin array is not proof either. But a datacentre IP, plus a canvas hash shared with two hundred other sessions this hour, plus a timezone that contradicts the IP geolocation, plus perfectly uniform 800ms gaps between requests, is not four weak signals. It is one very strong one, because the combination is far rarer than any part.

The three layers you are actually being read at

It helps to separate the surface into layers, because each one is defended and defeated differently.

Network layer. The IP address and everything derivable from it: ASN, whether the range is classified as datacentre, residential, mobile or hosting, its historical abuse reputation, the geolocation, whether a thousand other sessions are arriving from the same /24 this minute. This is the layer proxies address, and it is the only layer they address.

Transport and protocol layer. Before a single byte of your HTTP request is parsed, the TLS handshake has already described your client. The ordered list of cipher suites, the extension list and its order, supported groups, signature algorithms — hashed together these produce a JA3 or JA4 fingerprint that is characteristic of the TLS library, not of the User-Agent string you are about to send. Above that, HTTP/2 contributes its own signature: the SETTINGS frame values and their order, the window update size, the priority tree, and the pseudo-header order. A Python client claiming to be Chrome 141 is caught here, before any JavaScript runs, because OpenSSL and BoringSSL do not shake hands the same way.

Browser layer. Everything a page can measure once it executes: the JavaScript environment, rendering behaviour, hardware hints, and how the pointer and keyboard behave over time. This is where fingerprinting proper lives, and it is the layer that cannot be proxied away. You can put a perfect residential IP in front of a browser that renders canvas like a GPU-less Linux container and you will still be scored as automation.

Most teams are excellent at layer one, competent at layer two by accident (because they use a real browser), and blind at layer three. That distribution explains an enormous amount of unexplained blocking.

How browser fingerprinting actually works

A fingerprint is not one value. It is a vector of measurements, hashed into something short enough to store and compare. The art on the defensive side is choosing measurements with two properties at once: entropy, meaning the value differs meaningfully between machines, and stability, meaning it stays the same for the same machine across sessions, reboots and IP changes. A signal with high entropy and low stability is useless for linkage. A signal with high stability and low entropy tells you nothing. The ones that have both are the ones that get you caught.

If you have never seen your own entropy quantified, run your everyday browser through the EFF's Cover Your Tracks once. It reports how many bits each attribute contributes and how rare your combination is among their sample. It is a research tool rather than a scraping test, but it makes the concept concrete in a way no article can.

Passive signals: what you send before you say anything

Passive signals require no JavaScript. They are read from the connection and the request itself.

Header order is the classic. Real Chrome emits its request headers in a specific, consistent sequence; requests libraries emit theirs alphabetically or in insertion order. You can set every header value correctly and still be obviously synthetic because they arrive in the wrong order. The same applies to header casing in HTTP/1.1 and to which headers are present at all — a browser that claims to be Chrome but never sends sec-fetch-site or sec-ch-ua is not Chrome.

Client Hints deserve specific attention because they are where user-agent entropy is migrating. Chromium has been freezing and reducing the legacy User-Agent string for years, moving the detail into Sec-CH-UA headers and the navigator.userAgentData API, including high-entropy values you must explicitly request. A scraper that spoofs the User-Agent string but leaves Client Hints untouched, or sets them to a version that contradicts the UA, has manufactured a contradiction that did not exist before it interfered.

Active signals: what the page measures

Once JavaScript runs, the measurement surface widens dramatically.

Canvas. The page draws text and shapes to an offscreen canvas, reads the pixels back and hashes them. The output depends on the GPU, the driver version, the rasteriser, font hinting settings and anti-aliasing behaviour. Two machines with the same OS and browser version but different graphics stacks produce different hashes. It is the single most valuable fingerprinting signal in existence: high entropy, extremely stable, cheap to collect.

WebGL. Two things are read here. The UNMASKED_VENDOR_WEBGL and UNMASKED_RENDERER_WEBGL strings, which name your graphics adapter in plain text, and a rendered-image hash of a shader-drawn scene, which behaves like canvas but with more variance. Beyond that, the supported extension list, shader precision formats and maximum texture sizes all differ by driver.

Audio. An OfflineAudioContext runs an oscillator through a compressor and the resulting float samples are summed. Different DSP implementations and floating-point paths produce measurably different sums. Lower entropy than canvas, but very stable and very cheap.

Fonts. The page measures the rendered width of a string in a long list of candidate fonts and records which ones actually resolved. Your installed font set is a surprisingly personal thing — it reflects your OS version, your locale, your Office install, the design tools you use. On top of that, some browsers expose font enumeration APIs directly.

Hardware and environment. navigator.hardwareConcurrency, navigator.deviceMemory, maxTouchPoints, screen.width/height, availWidth, devicePixelRatio, colour depth, the delta between window.outerHeight and window.innerHeight (which reveals the browser chrome height and therefore the toolbar configuration), the list of media devices, codec support via canPlayType and MediaCapabilities, the installed speech synthesis voices — that last one is startlingly identifying and almost nobody spoofs it.

Locale and time. Intl.DateTimeFormat().resolvedOptions() gives the timezone, calendar and numbering system. Date.prototype.getTimezoneOffset gives the offset. navigator.languages gives the accept-language preference order. Three separate places to contradict yourself.

WebRTC. Left unmanaged, the ICE candidate gathering process reveals your local network addresses and, more damagingly, your real public IP even when the page itself was fetched through a proxy. This is the leak that makes people think their proxy is broken when it is working perfectly.

If you want a gentler walkthrough of these mechanics before going deeper, Browser Fingerprinting Explained for Beginners covers the same ground at a slower pace.

Behavioural signals

The final tier is not about the machine at all. Mouse movement curvature and acceleration. Whether a click was preceded by a mousemove. Keystroke inter-arrival timing and the natural variance in it. Scroll velocity profiles. Time-on-page distributions. Whether the pointer ever moves without a subsequent event.

Behavioural analysis is expensive to run and easy to get wrong, so it is usually reserved for high-value flows — checkout, login, account creation. But it is also where naive automation is most obviously synthetic. A perfectly linear mouse path from (0,0) to a button centre in three frames is not something a human hand produces.

The detection landscape, in one table

These vendors overlap heavily and all of them read all of the layers. What differs is emphasis, which affects what tends to break first.

Vendor Leans hardest on What usually trips a scraper Typical response
Cloudflare Bot Management TLS/HTTP2 fingerprint, JS challenge, IP reputation TLS signature contradicting the claimed browser Managed challenge, then 403
Akamai Bot Manager HTTP/2 frame fingerprint, its own sensor payload, behaviour Missing, stale or replayed sensor data 403, or a fake 200 with useless content
DataDome Device coherence across canvas/WebGL/audio, device history Impossible hardware combinations, datacentre ASN Captcha, then device-level ban
HUMAN (PerimeterX) JS environment integrity, interaction entropy Overridden natives whose toString is not native code Block page with a support code
Kasada Client-side VM, anti-tamper, client proof-of-work Any runtime patching; headless CPU timing profile 429/403 before content is served
In-house (marketplaces, social platforms) Account graph joined to device fingerprint Two accounts sharing one canvas hash or one cache Account linking, checkpoint, ban wave

The last row is the one that matters most to anyone whose collection involves logged-in sessions, because the consequence is not a failed request. It is a lost account, and often several at once when the platform back-fills the link across your whole estate. That failure mode is why managing multiple accounts safely is a different discipline from anonymous scraping, even though the underlying signals are identical.

Why headless browsers get caught

Headless Chrome used to announce itself. navigator.webdriver was true, the User-Agent literally contained the word HeadlessChrome, navigator.plugins was empty, window.chrome was missing, and the Permissions API contradicted Notification.permission. Detection was a one-liner.

That era is over. The new headless mode shipped in Chromium is the same browser binary running the same rendering path, not the separate reduced implementation it replaced. Most of the old tells are simply gone. But three structural problems remain, and they are harder to fix than the old ones.

There is no machine underneath

A headless container typically has no GPU, so WebGL falls back to a software rasteriser and the renderer string says so in plain English. It has a minimal font set, because nobody installs fonts on a scraping node. It has no display server, so the window geometry is synthetic and the outer/inner height delta is often exactly zero. It has no audio hardware, no cameras or microphones to enumerate, no DRM modules, and frequently a suspiciously round hardwareConcurrency. None of that is a bug in the browser. It is an accurate description of a server, and the fingerprint reports it faithfully.

Patching is louder than the leak

This is the part that surprises people. Once you start overriding properties from JavaScript, you create a second, richer class of evidence: evidence of tampering. A getter defined by a page script does not stringify to function () { [native code] } unless you have gone to considerable trouble. Property descriptors change. Prototype chains gain unexpected own-properties. Object.getOwnPropertyNames on the window returns things it shouldn't. Error stack traces gain frames from your injection. Function .length and .name drift.

Detectors know the popular stealth plugins by name and test for the exact shape of their patches. A machine that honestly reports a software renderer scores worse than a real user. A machine that dishonestly reports an RTX 4090 through a patched getter scores worse than both, because the first is unusual and the second is provably lying.

This is the strongest architectural argument for applying fingerprint values inside the browser engine itself rather than injecting JavaScript into the page. When the value is produced by the C++ that would have produced the real value, there is no override to detect, no prototype to inspect and no stack frame to find. It is also why the property applies uniformly inside Web Workers, iframes and service workers — a place where injection-based approaches routinely miss and get caught by a detector that simply reads the same value from two contexts and compares.

The automation channel itself is observable

Attaching a debugger to a browser is not free. Certain CDP domains change runtime behaviour in ways a page can notice — the long-known trick of overriding Error.prototype.stack's getter and watching whether merely serialising an error triggers a console binding is the canonical example. Puppeteer and Playwright also leave characteristic patterns in how they dispatch input events and manage targets.

The mitigations are architectural rather than cosmetic: drive the browser over raw CDP using only the domains you genuinely need (DOM, Input, Page, Network, Target), never enable the runtime domain on a page you care about, and dispatch input as trusted events at the browser level so that isTrusted is true and the event sequence looks like a real pointer. Dual Login's automation layer is built on exactly that constraint — no runtime domain on the driving path, and navigator.webdriver stays false because nothing ever set it.

Consistency beats sophistication

Here is the thing I would put on a poster if I could only keep one lesson from this article: at scale, you are almost never caught by a bad value. You are caught by an impossible combination.

Real-world examples I have watched kill production jobs:

  • A User-Agent claiming macOS while navigator.platform returned Win32, because the spoofing layer covered one and not the other.
  • A User-Agent claiming Chrome 141 while Sec-CH-UA still advertised 138, because the UA was templated and the hints were not.
  • A WebGL renderer string naming a high-end discrete GPU alongside deviceMemory: 2 and hardwareConcurrency: 2. Nobody pairs a flagship card with a two-thread machine.
  • Intl reporting Europe/London on a request egressing from a São Paulo residential IP, with Accept-Language: en-US. Three different countries in one identity.
  • maxTouchPoints: 10 on a desktop User-Agent with a fine pointer and hover support advertised in media queries.
  • Five hundred sessions with five hundred beautifully unique canvas hashes and one identical audio hash, because the audio spoof was seeded globally instead of per profile.

Every one of those was created by the anti-detection layer, not by the browser. The fix is never a better spoof. It is a coherent one.

Spend your entropy budget on being boring

Uniqueness is not the goal. Being unique is exactly what fingerprinting is designed to reward — a one-in-a-million device is trivially trackable across sessions even if nothing about it looks automated. What you want is to fall inside a large, populated cluster: a common GPU on a common OS with a common screen size and a mainstream font set. The most durable identity is the one shared by ten million ordinary people.

This is where randomisation goes wrong most often. Randomising every attribute independently produces combinations that no shipped machine has ever had. Randomising on every page load produces something worse: a device whose canvas hash changes mid-session, which is not a device at all. Noise must be per-profile stable — generated once, persisted with the profile, and identical on the thousandth launch. If you are working out how to do that properly, How to Change Browser Fingerprint walks through the practical mechanics.

The proxy-locale triangle

Three things must agree, always: the proxy exit location, the reported timezone, and the language preferences. A fourth, geolocation permission responses, must agree if you ever grant it. Getting this right is mostly bookkeeping — resolve the exit IP's country at launch, derive the timezone and locale from it, and let the fingerprint inherit those rather than being generated in isolation.

It is also the clearest illustration of why a VPN and an antidetect browser are not substitutes for one another. A VPN moves layer one and leaves layers two and three describing your actual laptop, identically, for every tab. If that distinction is fuzzy, Antidetect Browser vs VPN is the short version.

An architecture for collection at scale

Tier your targets before you spend anything

Not every page needs a browser. Cost per page varies by roughly two orders of magnitude across these tiers, and most teams simultaneously overspend on the easy end and underspend on the hard end.

Tier 0 — static HTML, no JavaScript requirement, no bot management. A plain HTTP client with correct header order and a decent TLS profile. Do not launch a browser for this; you are paying 200MB of RAM to parse a table.

Tier 1 — JavaScript-rendered content, light or no vendor protection. Headless browser, datacentre proxies, aggressive concurrency. Rendering is the cost, not evasion.

Tier 2 — real bot management in front. Full browser profile with a coherent fingerprint, residential or mobile egress, persistent cookies, sessions measured in hours rather than requests.

Tier 3 — anything behind a login, or anything where an account is the asset. One identity to one proxy to one storage jar, pinned permanently, warmed before use, never reused across accounts. This is antidetect territory, and it is where a mistake costs you an account rather than a request.

Treat the identity as one indivisible unit

An identity is five things bound together: the fingerprint, the proxy, the cookie and storage jar, the behavioural profile, and the schedule. They move together or they do not move.

The most common violation is swapping the proxy under a stable fingerprint — rotating IPs while everything else stays constant. From the defender's perspective this is a device that teleports between cities, which is a far stronger anomaly than either a suspicious IP or an unusual fingerprint alone. It is also self-defeating: you are handing over a stable cross-IP identifier and then demonstrating that you control a proxy pool.

Storage isolation belongs in the same bundle. Separate cookie jars are obvious, but the HTTP cache is a linkage vector too — shared ETags and cached resources let a site correlate profiles that share a cache directory. Genuine isolation means a separate user data directory per identity, which is exactly why Dual Login gives every profile its own directory on disk rather than sharing one browser install with a cleared cookie store.

Warm up, then stay warm

A brand-new identity that opens cold and immediately requests your highest-value endpoint is behaving like nothing on the real web. Real users arrive from somewhere. They have history, a referrer, a few dozen cached resources, a session cookie from last Tuesday.

The cheapest identity you will ever have is the one that already carries trust. Long-lived sessions are worth more than fresh ones, so persist storage properly, resume rather than recreate, and treat identity churn as a cost rather than a safety measure. This is the opposite of the instinct most people bring from the anonymous-scraping world.

Shape the rate per identity, not in aggregate

A global rate limit is close to meaningless. What is measured is per-device and per-IP cadence. Human request timing has heavy variance, long idle gaps, bursts while reading, and diurnal structure. Five hundred identities that all wake at 03:00 UTC, all fire at a uniform 800ms interval, and all go quiet at 03:40 form a single obvious cohort no matter how good each individual fingerprint is.

Add jitter with real variance, stagger start times across hours, and let identities have days off.

Build the error taxonomy before you need it

When blocking starts, the only thing that saves you is data you were already collecting. Log, for every failure: status code, challenge type, response size, whether it happened before or after JavaScript executed, identity ID, proxy ID, fingerprint cohort, and time-since-session-start.

That lets you classify failures instead of guessing:

  • Instant 403 before any JS runs — layer one or two. IP reputation or TLS mismatch. Proxies or client stack.
  • Challenge appearing only after the page executes — layer three. Fingerprint or environment integrity. More proxies will not help.
  • Works for twenty minutes then degrades — rate or behaviour. Slow down, add variance.
  • Only logged-in requests fail — account-level. The device has been linked to something.
  • Recovers on its own after an hour — a soft rate limit, not a ban. Back off, do not rotate.

Without this taxonomy every failure looks the same and every fix looks like buying more IPs.

Testing without burning your estate

Fingerprint test pages — CreepJS, BrowserLeaks, Cover Your Tracks, and the various antidetect-oriented checkers — are useful for one specific job: finding internal contradictions. They will tell you that your platform disagrees with your User-Agent, that your WebGL renderer is a software rasteriser, or that your timezone and IP are in different hemispheres. Fix everything they flag.

What they cannot tell you is whether a real target will let you through, because a target scores you on things a test page has no access to: your traffic history, how many other sessions share your ASN right now, whether the account you are signing into was created from a different device fingerprint last month. A clean scoreboard is necessary, not sufficient. Any vendor that markets a green checker score as proof of undetectability is selling you a thermometer and calling it a cure.

The real test is a canary. Pick a low-value, low-traffic page on your actual target. Hit it on a fixed schedule with one identity per fingerprint cohort. Record the challenge rate. Change exactly one variable at a time — the OS in the fingerprint, the proxy type, the session length — and watch what moves. It is slow and it is boring, and it is the only method that produces knowledge specific to your targets rather than folklore.

Keep a cohort registry while you are at it: which fingerprint families are in use, how many identities share each, and what their current success rate is. When a cohort starts degrading, retire it wholesale rather than waiting for individual identities to die.

Staying on the right side of the line

A guide about detection that ignores legitimacy is incomplete, so briefly: RFC 9309 standardised robots.txt, and honouring it is both good manners and good engineering, because sites that see well-behaved crawlers are less aggressive with everyone. Terms of service are contracts and can be enforced as such. Personal data pulled from public pages is still personal data under GDPR and similar regimes, and does not become exempt because it was easy to fetch. Access to material behind an authentication wall you are not entitled to use is a different category of problem from anything discussed here, and no fingerprint fixes it.

None of that is legal advice. It is a note that durability and legitimacy correlate: collection that respects rate limits, avoids personal data it does not need, and does not degrade the target tends to survive for years, while abusive patterns get shut down regardless of how good the browser looks.

Where an antidetect browser actually fits

An antidetect browser is not a scraping framework, and anyone who tells you it replaces one is overselling. It is an identity layer. It is the right tool when the work is account-bound, when the target runs serious bot management, or when you need many long-lived, mutually unlinkable sessions that keep their logins. It is the wrong tool for pulling ten million static product pages, where a well-configured HTTP client is faster and cheaper by two orders of magnitude.

Within that scope, the design choices that matter are the ones this article has been circling:

The fingerprint should be applied by the engine, not injected into the page. Dual Login writes each profile's fingerprint into a signed, encrypted blob that the custom Chromium build reads at startup, so the values come out of the same native code paths that would have reported the real ones. There is no injected script, no patched prototype, and no toString mismatch to find — and the values are consistent inside workers and iframes, which is where injection-based tools most often disagree with themselves.

Every profile gets its own data directory. Cookies, localStorage, IndexedDB and cache are physically separate, so there is no shared-cache linkage and logins survive across restarts and across machines.

Proxies bind to profiles, including SOCKS and authenticated endpoints, with WebRTC masked to the proxy exit so the real address never leaks out of ICE, and timezone and language derived from the exit location rather than the host.

Automation runs over raw CDP without enabling the runtime domain, so clicks and keystrokes arrive as trusted browser-level events and the debugger's usual tells are absent.

If you are still choosing tools, the comparison pieces on GoLogin vs AdsPower and cheaper Multilogin alternatives are more useful than another feature grid, because what separates these products in practice is fingerprint quality and how honestly they handle the consistency problem — not the length of the feature list.

FAQ

Can a proxy alone stop web scraping detection?

No, and this is the single most expensive misconception in the field. A proxy changes the IP address, which is one signal out of dozens. It does nothing about your TLS fingerprint, your HTTP/2 frame signature, your canvas and WebGL hashes, your font set, your timezone or your request cadence. Worse, a proxy without fingerprint isolation actively hurts you: rotating IPs under a constant fingerprint proves you control a proxy pool and gives the defender a stable identifier to track you across all of them.

Generally yes, though it is regulated. Sites have legitimate security reasons to identify devices — fraud prevention, credential stuffing defence, abuse mitigation. In the EU and UK, fingerprinting for tracking or advertising purposes typically requires consent under ePrivacy and GDPR rules, and regulators have been explicit that fingerprinting is covered by the same rules as cookies. Fingerprinting purely for security is usually treated as a legitimate interest. The rules constrain the purpose, not the technique.

Does incognito or clearing cookies change my fingerprint?

Barely. Private browsing clears cookies and storage, which breaks cookie-based tracking, but your canvas hash, WebGL renderer, font list, screen geometry, audio signature and hardware hints are all identical because they describe the machine, not the session. That is precisely why fingerprinting became popular — it survives everything users do to reset their identity. Real separation requires different fingerprints and different storage, which is what per-profile isolation provides.

Why did my scraper start failing when nothing changed on my side?

Something changed on theirs. Vendors ship model updates continuously, and a change in weighting can push a cohort that was sitting just below the threshold over it. It can also be gradual accumulation — your identities built up enough history to be recognised as a cohort. Use the error taxonomy above to tell which layer broke: failures before JavaScript executes point at IP or TLS; challenges after execution point at the fingerprint; time-dependent failures point at rate and behaviour.

Are stealth plugins for Puppeteer and Playwright enough?

For lightly defended targets, sometimes. Against serious bot management, they tend to make things worse, because they patch a known list of properties in a known way and detectors test for the patches themselves. A property override that does not stringify as native code is a stronger and cleaner signal than the leak it was hiding. The structural fix is a browser whose values are genuinely different at the engine level, not a page script rewriting them after the fact.

How many profiles can one machine realistically run?

Each profile is a separate browser process with its own memory, so RAM is the binding constraint rather than CPU. As a rough planning figure, expect around five concurrent profiles per 4GB of RAM with memory-saving options enabled, more if your pages are light, fewer if they are heavy single-page applications. Scale beyond that by adding machines rather than cramming processes, since a machine under memory pressure produces timing artefacts that are themselves a behavioural signal.

Closing thought

The teams that do this well are not the ones with the cleverest evasion. They are the ones who stopped thinking about evasion and started thinking about coherence. Every identity they run describes a machine that could plausibly exist, connecting from a place that machine could plausibly be, behaving the way a person using that machine would behave — and then it keeps doing that consistently for months, which is the part that actually earns trust.

Get the fingerprint honest with itself, bind it permanently to its proxy and its storage, measure what fails and why, and most of the whack-a-mole disappears. The rest is engineering discipline.

If you want to try that model rather than read about it, Dual Login runs each profile as a genuinely separate browser — native engine-level fingerprinting, its own data directory, its own proxy, and automation that never touches the runtime domain. Spin up a handful of profiles, point them at your own canary page, and see what the numbers say before you commit anything real. Testing properly before you pay is the whole point.

Run every account like a separate device

Dual Login gives each profile a real fingerprint, its own proxy and sealed storage — free plan, no card required.