Detection run, August 2026 - one of the four checks blocked us
This is the first of a monthly series. The point of publishing it is that it is dated and repeatable, not that it is flattering. One of the four checks blocked us, and that is in the table with the rest.
Conditions
| Run started | 2026-08-02 00:18 UTC |
| Run finished | 2026-08-02 00:21 UTC |
| Kernel | Chrome 150 |
| Profile | freshly created, default settings, nothing tuned |
| Proxy | managed residential, exit 149.52.102.108, Houston US |
| Host | macOS, headful |
The profile was created and used once. No warming, no history, no per-site tuning. That is deliberately the weakest realistic case.
What the profile presented
User-Agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/150.0.0.0
platform Win32
languages en-US, en
timezone America/Chicago
webdriver false
hardware 8 cores, 16 GB, 1536x864
WebGL vendor Google Inc. (Intel)
WebGL render ANGLE (Intel, Intel(R) UHD Graphics 620 Direct3D11 vs_5_0 ps_5_0, D3D11-27.20.100.9316)
The proxy exits in Houston and the browser reported America/Chicago. That binding is automatic, and it is the single most common thing to get wrong by hand.
Results
| Check | Result | Detail |
|---|---|---|
| whoer.net | Pass | Disguise 100%. Proxy: No. Anonymizer: No. Blacklist: No. OS read as Win10.0, browser as Chrome 150.0 |
| CreepJS | Inconclusive | Page loaded (HTTP 200) and produced a fingerprint ID, but our harness failed to read its score fields. Our fault, not a result |
| DataDome (leboncoin.fr) | Blocked | HTTP 403, CAPTCHA challenge served, 1,206-byte body. No listings reached |
| Kasada (footlocker.com) | Pass | HTTP 200, real storefront rendered, 1.59 MB body, no interstitial |
The obvious question about that table is what the free alternative scores on the same checks. We measured that later in the month, against the same targets through the same proxy, and it is in the "Baseline" section at the bottom of this page.
An incidental observation: STUN did not resolve
Not one of the four checks, but it showed up in the kernel log repeatedly during the run and is worth recording:
Failed to resolve address for stun.l.google.com., errorcode: -105
STUN resolution failed through this proxy for the whole session, so WebRTC
gathered no server-reflexive (srflx) candidate. That is not a leak, which is
the failure mode people usually worry about, and it is not a clean pass either.
A browser that produces no srflx candidate at all is its own small signal, and
we are recording it rather than filing it under "no WebRTC leak detected".
We did not design the run to measure this and have not characterised how often it happens, so treat it as an observation, not a result. It is on the list for the September run.
Update, 2026-08-13: that observation did not survive being measured. The
harness now probes WebRTC deliberately instead of reading the kernel log, and on
every run since, the browser did gather a srflx candidate and it was the proxy
exit address, with the host's own address appearing nowhere. The resolver error
still shows up in the log. Whatever it costs, it is not the absence of a
candidate, so treat the paragraph above as describing one session rather than
this proxy in general.
About the DataDome failure
Read that row again, because it is the interesting one.
leboncoin.fr returned 403 with a CAPTCHA. The profile did not get through. A previous capture of this same target, taken weeks earlier and still shown on our homepage at the time of writing, showed it passing. Today it does not.
We went and measured it rather than leaving it at a guess. The rerun at the bottom of this page put twelve exit addresses through the same target on 2026-08-13, our kernel and a stealth-plugin Playwright paired inside each address neighbourhood: the exit address decided five rounds out of six, and the browser decided at most one. The general answer is that a 403 here is first a question about the address and only after that a question about the browser.
What that does not do is explain this particular run backwards. A different
address in the same 149.52.* block passed eleven days later, so we cannot
point at the address we used on 2026-08-02 and call it burned. The observations
below are what we could honestly say at the time, before any of it was measured,
and they stay as written:
- A single run is a single data point. Same profile, same proxy, an hour later, could differ. That is what makes a permanent "undetectable" badge dishonest, in either direction.
- The proxy is the most likely factor. This exit is a shared residential address. DataDome scores IP reputation heavily, and we did not check this address's history before the run. A burned exit fails before the fingerprint is ever evaluated. (This is the one the rerun bore out.)
- A brand-new profile with zero history is the weakest case. Real usage carries accumulated cookies and behaviour that a first-visit profile does not have.
- It might simply be that their detection improved. That happens, and pretending otherwise would make every future report worthless.
What we can say is narrower and more useful: the fingerprint layer looked coherent (whoer 100%, proxy undetected, no automation tells), and one commercial wall let it through while another did not.
The CreepJS gap, and a correction to what we first said here
CreepJS loaded and returned a fingerprint ID, so the browser reached it. Our extraction script failed to pull the score fields out of the rendered page, so we have no numbers to publish and we are not going to estimate them.
The first version of this page called that a harness bug and said it would be fixed for the September run. That was too confident, and this is the correction. We went and tried. The extraction was genuinely wrong, CreepJS renders its scores into shadow roots rather than the document body, but fixing that exposed a larger problem: on our test host, CreepJS reliably takes the renderer down with it, and when the renderer dies the debugging connection and the whole run die too. Nine attempts, several distinct failure modes, no numbers.
CreepJS now runs in its own isolated invocation so that when it crashes it costs only itself instead of every check after it. Whether we can capture its scores at all in September is genuinely open. If we cannot, that row will say so again rather than quietly disappear.
Method, so you can repeat it
- Create a profile in the desktop app or via
openProfile(). Change nothing. - Bind a residential proxy. Timezone, locale and geolocation follow the exit IP automatically.
- Open, in one session: whoer.net, abrahamjuliot.github.io/creepjs, leboncoin.fr, footlocker.com.
- Record HTTP status and the on-page result for each.
Every target is public and none of them require our cooperation. The free tier is unlimited local profiles with no card, so running this yourself costs an afternoon.
If your result differs from ours, ours is the one to distrust: we ran one profile through one proxy from one machine.
What we changed because of this run
Our homepage carries a DataDome screenshot from an earlier capture showing a pass, and this run contradicted it, so we labelled that screenshot as stale and pointed it here.
We have since put the label back to a plain pass, and that deserves an explanation rather than a quiet edit. The rerun at the bottom of this page found the exit address deciding five rounds out of six, with the same address blocking both browsers and a different address letting both through. Under that reading, one 403 from one address is not evidence that the capture was wrong, and marking it stale implied a certainty in the other direction that we did not have either. The screenshot still shows one good day, and it still links here.
Reproduced later the same day
We re-ran the same profile and proxy against the same targets a few hours after the run above. whoer returned 100% disguise again, and leboncoin.fr returned 403 with a CAPTCHA a second time. A twice-reproduced block on the same day with the same exit IP is weak evidence about DataDome in general and reasonable evidence that this specific result was not a fluke.
The repeat run did not finish: the Kasada target, which completed in the first run, took the process down in the second. These pages are heavy and our harness is not yet robust against that. Recorded here because a report that only mentions the runs that worked is the thing we are trying not to publish.
Baseline: playwright plus a stealth plugin
The comparison worth publishing is not another paid vendor. It is Playwright with a stealth plugin, which is free, and which is what most people weigh us against. So we ran it through the same four checks.
This part was measured on 2026-08-13, not on 2026-08-02, so it does not compare with the table above. It compares with itself.
Both arms ran on the same host, minutes apart, through the same residential exit. The stateless checks are below; DataDome needed a different design and has its own section after it.
| AntiBrow | Playwright + stealth plugin | |
|---|---|---|
| Engine | our kernel, Chrome 151 | Playwright's bundled Chromium, Chrome 148 |
| Fingerprint applied | in the engine | from injected JavaScript |
| whoer.net disguise | 90% | 90% |
| whoer read the OS as | Win10.0, matching the UA | Mac OS X, the host |
| whoer proxy detected | No | No |
| Kasada (footlocker.com) | Pass, 200, storefront rendered | Pass, 200, storefront rendered |
WebRTC srflx candidate |
the exit address | 202.66.x.x, the host's own address |
| Timezone vs exit | America/Los_Angeles, matches |
Asia/Shanghai, the host's |
navigator.languages |
en-US, en |
zh-CN, zh, the host's |
Whoer's headline number did not separate them, and Kasada let both through. If you came here for a chart where the paid product passes and the free one fails, this is not it.
The separation is in the bottom three rows, and it is not a score, it is
coherence. The baseline announced a Las Vegas exit address while reporting the
timezone of a machine in Asia, the language of that machine, and a WebRTC
candidate carrying its real address. Any one of those is a single query for a
site that cares. The plugin did its job, which is hiding the automation tells:
webdriver was false and whoer read it as an ordinary Chrome on macOS.
One detail says the rest. whoer served the baseline its Chinese page, because
Accept-Language was the host's. We noticed because the score extraction, which
looks for the English label, came back empty.
What this does and does not show:
- Every one of those three gaps is fixable by hand. Playwright takes
timezoneIdandlocale; WebRTC can be forced through a policy switch. The question was never whether it is possible, it is who keeps the list current across Chromium releases and adds the next entry to it. - It is one run, one exit, one host. These rows are properties of the two browsers rather than of the address, which is why one exit is enough for them and was not enough for DataDome.
- The baseline was left unhelped on purpose. No timezone or locale override. Handing it our proxy wiring would have measured our wiring twice.
The exact packages: playwright-extra with puppeteer-extra-plugin-stealth,
current versions, default configuration, headful, same residential exit.
DataDome needed a different experiment, and the first one was wrong
DataDome is stateful per exit address, which makes it the one check that cannot be run the way the rest of this page was run. Our first attempt put both arms through the same exit a few minutes apart, both were blocked, and we nearly published that as a tie. It is not a result: the second visitor from an address that has just been used on the same target is in a different situation from the first, and a shared address also means a shared reputation, so a single bad address blocks both arms and looks like agreement.
The redesign, one round per address neighbourhood:
- every visit gets its own exit address, never reused for any purpose,
- both arms in a round draw different addresses from the same /16, so the neighbourhood is held constant instead of being a coin flip,
- the arm that goes first alternates by round,
- each kernel visit uses a fresh profile, since one previous visit leaves cookies that make the next one a returning visitor.
Six rounds, twelve addresses, leboncoin.fr:
| Address block | AntiBrow | Playwright + stealth |
|---|---|---|
149.40.* |
Pass | Pass |
149.87.* |
Pass | Pass |
149.52.* |
Pass | Pass |
149.88.* |
Pass | Pass |
148.135.* |
Blocked | Blocked |
192.200.* |
Pass | Blocked |
| 5 / 6 | 4 / 6 |
The address decided five of the six rounds, and the browser decided at most one. Five pairs agreed with each other and disagreed with each other's neighbourhoods. One pair split, in our favour. One split out of six is not evidence of anything; if we published 5/6 against 4/6 as a win, the next round could reverse it and we would deserve to be quoted on it.
What this does establish is the thing worth knowing before you spend a day
debugging a fingerprint: on this check, the exit address dominated everything
else we varied. A 403 with a CAPTCHA is first a question about the address,
and only after that a question about the browser.
Two limits on reading it further. This is the homepage only, and login, search and checkout endpoints are scored far more harshly. And the baseline here is Playwright with the stealth plugin, which specifically hides the automation tells; unpatched automation is a weaker opponent and would not have done this well.
A note on our own number
Our arm scored whoer 90% on 2026-08-13 and 100% on 2026-08-02. Same product, different exit address, and the baseline scored the same 90% through that same exit on the same afternoon. We do not know which check whoer docked, and we are not going to guess at it in a direction that flatters us. It is the reason this series publishes the exit address next to the score.
The kernel also differs between the two runs, Chrome 150 in August and Chrome 151 in the baseline pair, because new profiles bind to the newest published build rather than a frozen one.
Next run: 2026-09-01. Same method, now with the baseline arm alongside. CreepJS isolated so it cannot cost the other checks, and no promise that it will produce a number.
See what a profile actually is or check yours against the same list.
How to cite this run
The results on this page are CC BY 4.0. Attribution is the licence condition, and a link is how attribution is normally given - so here it is, ready to paste.
AntiBrow, “Detection run, August 2026,” 2026-08-02.
<a href="https://antibrow.com/reports/detection-2026-08">antibrow.com/reports/detection-2026-08</a>
(CC BY 4.0) AntiBrow. Detection run, August 2026 (2026-08-02). https://antibrow.com/reports/detection-2026-08. CC BY 4.0. @misc{antibrow_2026_08,
title = {Detection run, August 2026},
author = {AntiBrow},
year = {2026}, month = {8},
url = {https://antibrow.com/reports/detection-2026-08},
note = {CC BY 4.0}
} Both files are served with Access-Control-Allow-Origin: *, so they can be fetched straight from your own page.
Try it on the free tier.
Unlimited local profiles, no credit card. Check it against the detectors yourself.