In the last post I benchmarked seven stealth browser tools against six public fingerprinting pages and found that four of them were indistinguishable from my real Chrome when running headed. Every one of those runs happened on a Windows laptop with a real GPU, a real display, and a residential IP.
Then I ran the identical harness on a 40-core Xeon VPS, in Docker, and six of the seven tools stopped passing.
Not because the tools changed. The code, the versions, the configuration and the test pages were all the same. What changed was the machine underneath, and the machine turns out to matter more than the tool. This post is the server half of that benchmark: what breaks, why, which signals are actually doing the work, and what you can realistically do about it.
If you have ever shipped a scraper that worked perfectly in development and got blocked within an hour of deploying, this is the gap you fell into.
The Setup
Everything ran in a Docker container on a VPS: 40 vCPU Xeon E5-2670 v2, 251 GB RAM, Ubuntu 24.04 in the container. Same harness, same package versions, same three repetitions per configuration, same six pages. 45 runs, zero failures, plus a second pinned-CPU run described later.
Three deployment details that matter more than they look:
Chrome is 153 on the server and was 152 on the laptop. Google's apt repo only serves current stable, so I could not pin it. Within a machine every tool used the same Chrome, so the tool-versus-tool comparisons are sound. Laptop-versus-server comparisons carry that one confound, and I will flag it where it could matter.
"Headed" on a server means Xvfb. There is no display, so the headed runs use a virtual one (xvfb-run -s "-screen 0 1920x1080x24"). That is the normal production setup, and it means the browser is a real windowed Chrome that happens to draw into a virtual framebuffer.
Chrome's sandbox stays on. Docker's default seccomp profile blocks the syscalls Chrome's sandbox needs, so most scraping images just pass --no-sandbox. I did not want to measure a browser running in a configuration I would not deploy, so the container runs as an unprivileged user with --security-opt seccomp=unconfined, which lets the real sandbox initialise. If you see "Failed to connect to browser" in Docker, that is what you are hitting.
Every Tool Fails the Same Check
Before any per-tool differences, one result applies to all of them.
Every Chromium-based tool fails sannysoft's WebGL Renderer check on the server, and in headed mode fails WebGL Vendor too. On the laptop, not one tool failed either.
The reason is not stealth. There is no GPU, so Chrome falls back to a software rasteriser and reports it honestly. A real desktop reports something like ANGLE (Intel, Intel(R) UHD Graphics...). A GPU-less server reports SwiftShader. That string says "datacenter" as clearly as an ASN lookup, it requires no JavaScript trickery to read, and no stealth tool in this test fixes it, because it is a property of the hardware rather than of the automation.
The one partial exception is interesting. Camoufox fails both WebGL checks headless but passes them headed under Xvfb, which is consistent with its fingerprint injection having something coherent to work from once a display exists. It is the only tool where the WebGL column changes at all.
Everything Is a Bot Now
Here is the same measurement as part one, run on the server. "Clean" means it passed all five detection pages.
On the laptop, six of seven tools were clean headed. On the server, exactly one is: Camoufox. Every Chromium tool is flagged, including headed, and including the hardened configuration that matched my real browser exactly in part one.
The full numbers, medians of three runs:
| Tool | Mode | sannysoft | CreepJS headless | are_you_a_bot | BrowserScan | fingerprint-scan |
|---|---|---|---|---|---|---|
| Playwright | headed | 2 fail | 33% | bot | Robot | 100 |
| Patchright | headed | 2 fail | 0% | bot | Normal | 100 |
| SeleniumBase | headed | 2 fail | 0% | bot | Normal | 100 |
| Botasaurus | headed | 2 fail | 0% | bot | Normal | 94 |
| Zendriver | headed | 2 fail | 0% | bot | Normal | 100 |
| Nodriver | headed | 2 fail | 0% | bot | Normal | 94 |
| Camoufox | headed | 1 (false positive) | 0% | human | Normal | 45 |
| Playwright | headless | 5 fail | 100% | bot | Robot | 100 |
| Patchright | headless | 4 fail | 67% | bot | Robot | 100 |
| SeleniumBase | headless | 1 fail | 33% | bot | Normal | 100 |
| Botasaurus | headless | 1 fail | 33% | bot | Normal | 100 |
| Zendriver | headless | 1 fail | 33% | bot | Normal | 100 |
| Nodriver | headless | 4 fail | 67% | bot | Robot | 100 |
| Hardened (UA + screen flags) | headless | 1 fail | 0% | bot | Normal | 100 |
| Camoufox | headless | 3 fail | 0% | human | Normal | 45 |
Two things are worth separating here.
The tool-level differences still exist and still behave exactly as they did on the laptop. Nodriver and Patchright still leak HeadlessChrome and still get caught by BrowserScan. The hardened config still drives CreepJS's headless score to 0% where stock Zendriver sits at 33%. Everything I found in part one reproduced on different hardware, a different OS and a newer Chrome. The fixes work.
They are just no longer sufficient. A new signal fires underneath them.
The Signal That Fires on Every Chromium Run
On the laptop, deviceandbrowserinfo returned human for every tool except vanilla Playwright, and the flag list was empty. On the server, every Chromium run trips hasSuspiciousWeakSignals - headed, headless, stock, hardened, all of them. Camoufox never does.
That flag name is doing a lot of work, so I wanted to know what feeds it rather than guessing. The obvious candidate on this box was the CPU: the Xeon exposes 40 cores, and navigator.hardwareConcurrency reports all of them. Ordinary consumer machines report 4 to 16. Forty is not a laptop.
That is testable. Docker's --cpuset-cpus changes what the browser sees, so I reran with the container pinned to four cores, everything else identical, two repetitions.
Headed Zendriver flipped from bot to human, and its fingerprint-scan score fell from 100 to 69. Both replicates agreed exactly. The only difference between the two containers is how many CPUs were visible.
So core count is a real, load-bearing fingerprinting signal, and it is one that no tool in this benchmark exposes a setting for. You can spoof your user agent with a flag, your screen with a flag, your platform with Camoufox. There is no --hardware-concurrency equivalent; you change it by constraining the container, which most people size for throughput rather than for what it says about them.
Pinning cores did not rescue the headless runs, which stayed flagged at 100. Headless on a server has both problems at once: the environment signals and the headless signals stack.
Camoufox Survives, at a Price
Camoufox is the only tool that passes deviceandbrowserinfo on the server, in both modes, exactly as it did on the laptop. That is a genuinely strong result and it is consistent across every run.
But its fingerprint-scan score went from 5 on the laptop to 45 on the server, identical in all six server runs. It is passing the binary verdicts while the continuous score notices the environment. And there is a structural problem underneath that, which the server makes worse.
Camoufox rotates its spoofed OS per launch, drawing from real-world device distributions. On the server it presented as Windows or macOS while running on Linux, with Linux fonts, Linux WebGL, and a Linux TLS stack. On a Windows laptop that lie is small, because the host actually is Windows some of the time. On a Linux server it is a permanent mismatch between what the browser claims and what the machine underneath can demonstrate.
None of the pages I tested cross-checks that hard enough to reject it. A commercial vendor that correlates claimed platform against font metrics and GPU strings would have an easier time. I could not test that without attacking someone's live protection, so treat it as reasoning about mechanism, not as a measurement.
Performance: the Server Is Faster and Heavier
| Tool | Launch, laptop | Launch, VPS | Peak RSS, laptop | Peak RSS, VPS |
|---|---|---|---|---|
| Botasaurus | 1.5s | 0.8s | ~1.4 GB | ~2.3 GB |
| Patchright | 4.0s | 1.0s | ~1.6 GB | ~2.4 GB |
| Zendriver | 1.4s | 1.1s | ~1.4 GB | ~2.2 GB |
| Nodriver | 1.4s | 1.1s | ~1.4 GB | ~2.3 GB |
| SeleniumBase | 3.3s | 2.0s | ~1.4 GB | ~2.3 GB |
| Camoufox | 10.0s | 3.8s | ~1.0 GB | ~0.45 GB |
Launch times improved across the board, and Camoufox's 10 second startup - the main practical objection to it in part one - drops to under 4 seconds on server-class hardware. That materially changes the recommendation for anyone launching a browser per job.
Memory went the other way. Every Chromium tool uses roughly 60% more RSS on the server than on the laptop. My first guess was that Chrome scales its process count with the 40 visible cores, but the pinned 4-core run used the same ~2.3 GB, so that explanation is wrong and I am leaving the cause unidentified.
The number that matters for capacity planning is the last column: Camoufox uses about 0.45 GB against Chromium's 2.3 GB, roughly five times lighter. On a box where you are deciding how many concurrent browsers fit in RAM, that ratio decides your bill.
What This Actually Means for Deployment
Combining both halves of the benchmark, here is what I would actually do.
Run headed under Xvfb, not headless. It costs almost nothing on a server and removes an entire category of signals. Every headless run in this benchmark carried strictly more detection than its headed counterpart, on both machines.
Pin your containers to a believable core count. Four to eight cores looks like a computer someone owns. Forty looks like a datacenter, and that alone flipped a verdict here. It is one flag: --cpuset-cpus=0-3.
Accept that WebGL gives you away, or spend money on it. On a GPU-less box, SwiftShader is the honest answer and every Chromium tool reports it. The options are a GPU instance, a tool that spoofs at a level below JavaScript, or accepting the signal. There is no free fix.
Weigh Camoufox differently on a server than on a laptop. It is the only tool that survived, its startup penalty largely disappears on server hardware, and it uses a fifth of the memory. The cost is Firefox and a spoofed OS that does not match the host.
Expect the tool choice to matter less than the environment. Six tools with completely different stealth strategies produced nearly identical results on this machine. When you are flagged on a server and not locally, patching the tool is usually the wrong place to look.
What This Does Not Show
IP reputation is untested and probably dominates. Every server run came from the same datacenter IP. deviceandbrowserinfo states it uses fingerprinting only, but I cannot verify what fingerprint-scan or BrowserScan do with the address, so I cannot cleanly separate "datacenter machine" from "datacenter IP" in those scores. In production the IP is often the whole decision, and a residential proxy on a flagged machine would change these numbers in ways this benchmark cannot predict.
One provider, one CPU, one container runtime. A 40-core Xeon with no GPU is a specific machine. A 2-core cloud instance with a virtual GPU would score differently, and I would not extrapolate the exact numbers beyond this shape of box.
Chrome 153 on the server versus 152 on the laptop. Within-machine comparisons are clean; cross-machine ones carry that difference.
Still no real anti-bot vendors. These are fingerprinting pages, not Cloudflare, DataDome or Akamai, for the same reasons as part one: testing those means testing somebody's live protection, and the results would not be reproducible.
Key Takeaways
- The same tools that match a real browser on a laptop get flagged on a VPS. Six of seven went from clean to flagged with no code change.
- Every Chromium tool fails the WebGL checks on a GPU-less server because Chrome honestly reports SwiftShader. No stealth tool fixes hardware.
navigator.hardwareConcurrencyis a real signal. Pinning the container from 40 cores to 4 flipped a headed verdict from bot to human, reproducibly.- No tool exposes a setting for core count. You change it with
--cpuset-cpus, sized for what it says about you rather than only for throughput. - Camoufox is the only tool that passed on the server, in both modes, and its startup penalty mostly disappears on server hardware. The catch: it spoofs Windows or macOS on a Linux host, a structural mismatch no page here rejected but a cross-checking vendor could.
- Chromium uses ~2.3 GB per browser on the server against Camoufox's ~0.45 GB. That ratio sets how many you can run.
- Run headed under Xvfb. Headless carried strictly more detection in every single comparison, on both machines.
- Tool-level fixes still work and still reproduce. They are simply not sufficient once the environment is the thing being measured.
Testing was done against public fingerprinting test pages that exist for this purpose, on infrastructure I control. Nothing here targets a production site or a live protection system. Check that you have authorization before testing any system you do not own.