What Is Hardware Concurrency Spoofing and Why Does It Matter?
A hardware concurrency spoofing guide teaches you how to mask the value returned by navigator.hardwareConcurrency — a JavaScript property that exposes the number of logical CPU cores on your machine. Fingerprinting scripts combine this with navigator.deviceMemory, GPU data, and screen metrics to build a stable device ID. Spoofing it incorrectly — or not at all — makes your browser profiles trivially linkable. Below, we break down detection methods, working spoof code, consistency traps, and the antidetect shortcut that handles all of it automatically.

How navigator.hardwareConcurrency Works Under the Hood
The navigator.hardwareConcurrency property is defined by the HTML Living Standard. It returns an integer representing the number of logical processors available to the browser — usually matching your CPU’s thread count. On a machine with a 6-core/12-thread CPU, it returns 12. On a dual-core laptop, it returns 4.
Why Browsers Expose It
The original purpose was performance optimization. Web Workers and SharedArrayBuffers need to know how many threads they can reasonably spawn. Assigning 16 workers on a 2-core device wastes memory and increases context-switch overhead. The API gives developers a way to scale parallelism to the actual hardware.
How Fingerprinting Scripts Abuse It
Fingerprinting libraries like FingerprintJS, CreepJS, and Brave’s randomization audits read navigator.hardwareConcurrency alongside dozens of other signals. A 16-thread value on a claimed mobile device, or a sudden change from 8 to 4 between sessions, raises a flag. Combined with navigator.deviceMemory, WebGL renderer strings, and screen resolution, the concurrency value contributes to a composite hash that survives cookie clearing, incognito mode, and even VPN switches.
If you want to understand the full scope of how these signals combine, our deep dive on browser fingerprinting covers every vector in detail.
What navigator.hardwareConcurrency Actually Reveals About You
Direct Information Leakage
- CPU class — A value of 24 or 32 signals a high-end desktop or workstation. Values of 2–4 suggest a budget laptop or mobile device.
- Device type narrowing — Mobile phones almost never report more than 8. If your spoofed User-Agent claims iPhone but concurrency reads 16, the profile is immediately suspect.
- Cross-session stability — The value rarely changes on the same device (unlike IP, which changes on Wi-Fi reconnects). This makes it a strong anchor for persistent fingerprints.
Indirect Correlation Risks
Fingerprinting frameworks cross-reference concurrency with:
| Signal | Correlation With Concurrency | Mismatch Risk |
|---|---|---|
navigator.deviceMemory |
High — 16-thread CPUs rarely ship with 2 GB RAM | Critical |
| WebGL renderer/vendor | Medium — Intel UHD on a “32-core” machine is suspicious | High |
| Screen resolution | Low–Medium — 4K at 2 cores is unusual | Medium |
| User-Agent platform | High — iOS caps at 6–8 cores; claiming 16 breaks believability | Critical |
| AudioContext sample rate | Low — but combined, adds entropy | Low |
The WebGL fingerprinting guide explains how GPU renderer strings interact with hardware signals like concurrency to build a coherent device profile.
Spoofing Method 1: Object.defineProperty Override
The most common approach is to redefine the property on the Navigator prototype before any page script can read it.
// Spoof navigator.hardwareConcurrency to 4
Object.defineProperty(Navigator.prototype, 'hardwareConcurrency', {
get: function() { return 4; },
configurable: true
});
console.log(navigator.hardwareConcurrency); // 4
Strengths
- Simple and fast — one line of code
- Works in all Chromium-based browsers and Firefox
- Can be injected via browser extensions or CDP
Weaknesses
- Detectable via prototype chain inspection. Anti-bot scripts can check if the property descriptor’s
getis native code or a user-defined function. ThetoString()of a native getter returns"function get hardwareConcurrency() { [native code] }", while your override returns a generic function string. - Worker inconsistency. Web Workers have their own
navigatorobject. A simple main-thread override leaves Worker threads returning the real value — an immediate red flag.
Hardening the Override
// Patch toString to look native
const originalGet = Object.getOwnPropertyDescriptor(
Navigator.prototype, 'hardwareConcurrency'
).get;
Object.defineProperty(Navigator.prototype, 'hardwareConcurrency', {
get: function() { return 4; },
configurable: true
});
// Make toString report [native code]
const spoofedGet = Object.getOwnPropertyDescriptor(
Navigator.prototype, 'hardwareConcurrency'
).get;
spoofedGet.toString = () => 'function get hardwareConcurrency() { [native code] }';
spoofedGet[Symbol.toPrimitive] = () => 'function get hardwareConcurrency() { [native code] }';
This is better but still not bulletproof. Advanced detectors compare the getter reference against the original prototype chain or check Reflect.getOwnPropertyDescriptor.
Spoofing Method 2: Worker-Based Verification and Consistency
Fingerprinting scripts increasingly spawn Web Workers specifically to read navigator.hardwareConcurrency from a Worker context, then compare the result with the main thread. If they differ, the profile is flagged.
Patching Workers
You need to intercept the Worker constructor and inject your spoof into every Worker’s initialization code:
const targetConcurrency = 4;
const originalWorker = window.Worker;
window.Worker = function(scriptURL, options) {
// Create a blob that patches navigator before loading the real script
const patchCode = `
Object.defineProperty(navigator, 'hardwareConcurrency', {
get: () => ${targetConcurrency}
});
importScripts('${scriptURL}');
`;
const blob = new Blob([patchCode], { type: 'application/javascript' });
const blobURL = URL.createObjectURL(blob);
return new originalWorker(blobURL, options);
};
// Preserve constructor identity
window.Worker.prototype = originalWorker.prototype;
Limitations
- Module Workers (
type: 'module') don’t supportimportScripts. You need a different injection path for ESM-based Worker scripts. - Service Workers have their own registration lifecycle and can’t be intercepted the same way.
- SharedWorkers persist across tabs, making consistent spoofing across all open pages harder.
Spoofing Method 3: CDP page.addScriptToEvaluateOnNewDocument
Chrome DevTools Protocol offers the most reliable injection point for Chromium-based automation. The method Page.addScriptToEvaluateOnNewDocument runs JavaScript before any page code executes — including before DOMContentLoaded and before any fingerprinting library initializes.
// Puppeteer example
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.evaluateOnNewDocument((concurrency) => {
Object.defineProperty(Navigator.prototype, 'hardwareConcurrency', {
get: () => concurrency,
configurable: true
});
// Also patch WorkerNavigator for Worker contexts
if (typeof WorkerNavigator !== 'undefined') {
Object.defineProperty(WorkerNavigator.prototype, 'hardwareConcurrency', {
get: () => concurrency,
configurable: true
});
}
}, 4);
await page.goto('https://example.com');
Playwright Equivalent
// Playwright example
const { chromium } = require('playwright');
const browser = await chromium.launch();
const context = await browser.newContext();
await context.addInitScript((concurrency) => {
Object.defineProperty(Navigator.prototype, 'hardwareConcurrency', {
get: () => concurrency,
configurable: true
});
}, 4);
const page = await context.newPage();
await page.goto('https://example.com');
Why CDP Is Superior
- Timing guarantee — Runs before any page JavaScript, so fingerprinting scripts never see the real value.
- Context-wide scope — Applies to all frames, including iframes used by ad networks and analytics trackers.
- No extension fingerprint — Unlike browser extensions (which add detectable artifacts), CDP injection leaves no trace in the extension API.
For a full walkthrough of evading bot detection in automated contexts, see our anti-bot bypass guide.
Consistency: The #1 Mistake That Gets You Caught
Spoofing hardwareConcurrency alone is pointless — and often worse than not spoofing at all — if the value contradicts other hardware signals. This is where most guides fail and most users get flagged.
The Hardware Signal Matrix
A believable profile needs internally consistent values across every hardware-adjacent API:
| Spoofed Profile | hardwareConcurrency | deviceMemory | WebGL Renderer | Screen | Platform |
|---|---|---|---|---|---|
| Budget laptop | 4 | 4 | Intel UHD 620 | 1366×768 | Win10 |
| Mid-range desktop | 8 | 8 | NVIDIA GTX 1660 | 1920×1080 | Win11 |
| High-end workstation | 16 | 16 | NVIDIA RTX 4080 | 2560×1440 | Win11 |
| MacBook Air M2 | 8 | 8 | Apple M2 | 2560×1664 | macOS |
| Android mid-range | 8 | 4 | Adreno 619 | 1080×2400 | Linux armv8l |
Signals That Must Agree
- deviceMemory — Chrome exposes this as a bucketed value (0.25, 0.5, 1, 2, 4, 8). A 2-core machine with 8 GB reported memory is plausible. A 2-core machine with 16 GB is not impossible but statistically unusual enough to raise entropy.
- WebGL renderer string — Claiming 16 cores while the GPU reports “Intel HD Graphics 4000” (a 2012-era integrated GPU) is incoherent. Match the GPU to the CPU era.
- User-Agent and platform — iOS devices cap at 6–8 cores on the A-series/M-series chips. Claiming 16 on an iPhone User-Agent is instantly flagged.
- Performance.now() timing — Some detectors run a quick benchmark: if you claim 2 cores but parallel Worker execution times suggest 16 threads of real parallelism, the spoof is exposed. This is hard to fake without actually throttling your CPU.
Common Spoofing Mistakes (And How to Avoid Them)
Mistake 1: Spoofing Only the Main Thread
Overriding navigator.hardwareConcurrency on the main thread but forgetting Workers is the most common failure. Fix: use CDP injection or patch the Worker constructor as shown above.
Mistake 2: Using Impossible Values
Setting concurrency to 1 is suspicious — almost no real device has a single logical core in 2026. Setting it to 128 when your User-Agent claims Chrome on Windows is equally implausible. Stick to common values: 2, 4, 8, 12, 16.
Mistake 3: Static Values Across All Profiles
If you run 50 browser profiles and every one reports hardwareConcurrency: 4 with the same GPU, same resolution, and same fonts, you’ve created 50 identical “different” devices. Fingerprinting services notice statistical anomalies in fleet fingerprints.
Mistake 4: Ignoring the toString Trap
Anti-detect detection scripts check whether the property getter is native:
const desc = Object.getOwnPropertyDescriptor(
Navigator.prototype, 'hardwareConcurrency'
);
const isNative = desc.get.toString().includes('[native code]');
// If false → spoofing detected
Mistake 5: Forgetting iframe Contexts
Cross-origin iframes (common in ad tech) get their own Navigator object. A simple prototype override on the top frame doesn’t propagate to sandboxed iframes. CDP’s addScriptToEvaluateOnNewDocument handles this automatically; manual approaches require intercepting iframe creation.
Testing Your Spoof
After implementing any spoofing method, verify it actually works across all contexts:
Quick Console Test
// Run in DevTools console
console.log('Main thread:', navigator.hardwareConcurrency);
// Worker test
const workerCode = `postMessage(navigator.hardwareConcurrency)`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = (e) => console.log('Worker thread:', e.data);
Online Fingerprint Checkers
- BrowserLeaks.com — Shows hardwareConcurrency alongside other hardware signals
- CreepJS — Tests for spoofing inconsistencies and prototype tampering
- FingerprintJS Pro demo — Shows how your fingerprint hash changes (or doesn’t) after spoofing
- AmIUnique.org — Compares your fingerprint against their database for uniqueness
Automated Verification Script
// Run with Puppeteer after applying your spoof
const results = await page.evaluate(() => {
const checks = {};
checks.mainThread = navigator.hardwareConcurrency;
checks.deviceMemory = navigator.deviceMemory;
checks.platform = navigator.platform;
// Check getter nativity
const desc = Object.getOwnPropertyDescriptor(
Navigator.prototype, 'hardwareConcurrency'
);
checks.getterIsNative = desc.get.toString().includes('[native code]');
return checks;
});
console.table(results);
The Antidetect Browser Approach: Why Manual Spoofing Falls Short
Manual spoofing — whether via CDP, extensions, or script injection — requires you to maintain consistency across dozens of interdependent signals. Every browser update can break your overrides. Every new fingerprinting technique requires a new patch. And running multiple profiles means managing unique, internally consistent hardware configurations for each one.
This is exactly what antidetect browsers automate. Instead of patching individual APIs, they generate complete device profiles where every signal — concurrency, memory, GPU, fonts, canvas noise, WebGL parameters, timezone, locale — is internally consistent and matches real-world device distributions.
How Send.win Handles Hardware Concurrency
Sendwin Browser assigns each profile a coherent hardware fingerprint at the Chromium engine level. The concurrency value isn’t patched via JavaScript overrides that can be detected — it’s set at the browser implementation layer, so Navigator.prototype.hardwareConcurrency returns the spoofed value natively. Workers, iframes, and even toString() checks all see the same consistent value.
Each profile gets a unique combination of concurrency, device memory, GPU renderer, screen dimensions, and platform string that matches real device configurations. You don’t need to build or maintain a hardware matrix — the profile generator handles coherence automatically.
For teams managing multiple profiles, session isolation ensures that each profile’s hardware fingerprint, cookies, storage, and network identity are completely separated — no cross-contamination between sessions.
When to Use Manual Spoofing vs. an Antidetect Browser
| Scenario | Manual Spoofing | Antidetect Browser |
|---|---|---|
| Single automation script | ✅ Fine for simple cases | Overkill |
| 2–5 concurrent profiles | ⚠️ Manageable but fragile | ✅ Easier to maintain |
| 10+ profiles with unique fingerprints | ❌ Consistency nightmare | ✅ Purpose-built for this |
| Passing advanced bot detection | ❌ Requires deep expertise | ✅ Handles it by default |
| Team with shared profiles | ❌ No collaboration tools | ✅ Built-in sharing and sync |
Advanced Detection Techniques You Should Know About
Worker Timing Attacks
Some detection scripts measure actual parallel execution performance. They spawn N workers performing computation-heavy tasks simultaneously and measure completion times. If you claim 4 cores but 16 workers finish in roughly the same wall-clock time (because your actual CPU has 16 threads), the spoof is exposed. Defending against this requires actual CPU throttling — which isn’t practical for most use cases.
SharedArrayBuffer Contention Analysis
Using SharedArrayBuffer and Atomics, scripts can measure memory access contention patterns that reveal the true number of CPU cores. This is a newer technique that bypasses simple property overrides entirely.
Performance API Fingerprinting
performance.now() resolution and PerformanceObserver data can reveal scheduling patterns that correlate with core count. While browser vendors have reduced timer precision to mitigate Spectre-class attacks (rounding to 5μs or 100μs), some fingerprinting libraries find statistical patterns over multiple measurements.
Cross-API Entropy Correlation
The most sophisticated detectors don’t rely on any single signal. They compute an entropy score across all available hardware signals and flag profiles where the statistical distribution doesn’t match known device populations. Spoofing one signal perfectly while leaving others at default values increases entropy in a way that’s worse than not spoofing at all.
🏆 Send.win Verdict
Manual hardware concurrency spoofing works for single-script automation but collapses at scale. Maintaining consistent fingerprints across concurrency, device memory, GPU, screen, and platform — for every profile, through every browser update — is a full-time engineering job. Sendwin Browser generates coherent hardware profiles at the engine level, handles Worker and iframe consistency automatically, and passes native-code getter checks that JavaScript overrides can’t survive. With the Automation API available on both Pro ($6.99/mo annual) and Team plans, you can connect Puppeteer, Playwright, or Selenium to fully fingerprint-masked profiles without writing a single spoof script.
Try Send.win free today — 30-day trial, no credit card, 150 profiles on Pro.
Frequently Asked Questions
What does navigator.hardwareConcurrency return?
It returns an integer representing the number of logical processor cores available to the browser. On a 6-core CPU with hyperthreading, it typically returns 12. The value is read-only in normal browsing but can be overridden via JavaScript property redefinition or browser-level spoofing.
Can websites detect if I’ve spoofed hardwareConcurrency?
Yes, through several methods: checking whether the property getter is native code, comparing main-thread and Worker-thread values, running timing-based core-count benchmarks, and cross-referencing with other hardware signals like deviceMemory and WebGL renderer strings. Simple overrides are easy to detect; engine-level spoofing in antidetect browsers is much harder.
What’s the safest value to spoof hardwareConcurrency to?
Values of 4 or 8 are the safest because they match the most common real-world devices — mid-range laptops and desktops. Avoid values of 1 (no real device has a single-core CPU anymore) or above 24 (too distinctive). Match the value to your other spoofed signals: a mobile User-Agent should use 4–8, a desktop User-Agent can use 4–16.
Does hardwareConcurrency change in incognito mode?
No. Incognito mode does not change the reported core count — it isolates cookies and browsing history but leaves hardware APIs fully exposed. This is why hardwareConcurrency is a reliable fingerprinting signal even against privacy-minded users.
Do I need to spoof hardwareConcurrency in Firefox?
Firefox has a privacy.resistFingerprinting flag (part of the Tor Uplift project) that spoofs hardwareConcurrency to 2 automatically. However, this also changes dozens of other signals (timezone to UTC, window size to specific dimensions, etc.), which creates its own distinctive fingerprint. For selective spoofing, you still need manual overrides or an antidetect browser.
How does hardwareConcurrency interact with Web Workers?
Each Worker thread has its own navigator object with a hardwareConcurrency property. Fingerprinting scripts often read the value from inside a Worker to check if it matches the main thread. If you override only the main thread’s value, the Worker will report the real core count, exposing the spoof.
Can I spoof hardwareConcurrency with a browser extension?
Extensions can inject content scripts that override the property, but extensions themselves are detectable through the WebExtensions API, specific DOM artifacts, and resource timing. CDP-based injection is more stealthy because it leaves no extension fingerprint. For production use at scale, antidetect browsers handle the override at a layer below what page scripts can inspect.
Is hardwareConcurrency the same on all browsers?
The property is supported in all major browsers (Chrome, Firefox, Edge, Safari, Opera) and typically returns the same value across all of them on the same device. However, some browsers may cap or round the value — Safari on iOS, for example, reports the actual core count of the A-series chip. Cross-browser consistency makes it a strong fingerprinting signal precisely because it’s hardware-dependent, not browser-dependent.
How Send.win Helps With Hardware Concurrency Spoofing Guide
Send.win is an antidetect browser built for exactly this kind of work — every profile is a clean, isolated identity:
- Isolated profiles – unique fingerprint, separate cookies and storage per profile
- Stealth engine – canvas, WebGL, fonts, and audio spoofed at the engine level
- Desktop app + cloud sessions – native app for Windows, macOS, and Linux, or run profiles in the cloud with no install
- Built-in residential proxies – with automatic timezone, locale, and WebRTC matching
- Team features – share logged-in profiles with teammates without sharing passwords
Try the instant cloud browser demo — no install, no signup — or download the desktop app. The 30-day free trial needs no credit card, and paid plans start at $6.99/month billed annually (see pricing).