How we decide what counts
The whole ranking is one multiplication. One half is an assumption about how common your value is; the other is a judgment about how hard a challenge acts on it. Both are printed on the row, and both are on this page in full.
The whole method
rank = surprisal of your value × challenge weight
Nothing else contributes. There is no hidden term, no per-visitor tuning, no model, and no comparison against anybody. Both factors are constants published on this site, and the reading prints them on every row it produces.
Worked, on a real row
Take the row most ordinary VPN users trip. This site assumes that 0.06 of browsers exit in a country other than the one their clock is set to — that is the assumption “exit country differs from the browser's time-zone country”, and its reasoning is on the assumptions page.
Surprisal is −log₂ of that, which is 4.06 bits. The published weight for “exit country disagrees with clock or language” is 0.80. Multiplied, the row ranks at 3.25, which fills 4 of the twelve dots on its ramp and prints the words “medium contribution” beside it.
You can do that arithmetic yourself with a calculator, and if you think either number is wrong, you know exactly which one to argue about. That is the point of publishing them.
Weight is on a scale where a browser that declares itself automated is 1. It is this site's judgment of how much a challenge acts on the signal, formed from published vendor documentation and the observable behaviour of the challenges themselves. There is no dataset of challenge outcomes behind it, and there will not be one until somebody publishes one. 5 of the 16 are marked as probably wrong.
-
A declared automation flag
1.00navigator.webdriver, ChromeDriver artifacts and a headless user-agent string are the three things every published anti-bot SDK reads first, and they are the only signals on this list whose meaning is unambiguous. A browser that declares itself automated is not being misread.
-
Two signals that cannot both be true
0.85A contradiction is evidence of intent in a way that rarity never is. A Direct3D renderer on a browser claiming macOS is not an unusual machine, it is a rewritten value, and vendors act on it hard.
-
Exit country disagrees with clock or language
0.80Vendors compare the request's egress country against the browser's own declared time zone and language because the comparison is cheap, server-side, and hard to fake from the page. It is also the single most common thing an ordinary VPN user trips.
-
Shared or hosting-network exit address
0.75Reputation scoring by address and by autonomous system is the oldest layer in this stack and the one a visitor has least control over. A hosting ASN is treated far more harshly than a residential one, and carrier-grade NAT puts thousands of unrelated people behind one address.
We think this one is probably wrong
Probably too flat. A datacentre ASN and a mobile carrier NAT are both 'shared' and are almost certainly not weighted the same by any real vendor. -
Software graphics instead of a GPU
0.60SwiftShader and llvmpipe are what a browser falls back to inside a container, and vendors know it. It is also what a laptop with hardware acceleration switched off looks like, which is why this row exists at all.
-
Missing the challenge's JavaScript deadline
0.55Interstitials give the page a few seconds to finish a computation and report back. A machine that misses the deadline is indistinguishable, to the challenge, from one that refused to run it. This is the cause behind most 'checking your browser' hangs.
-
A content blocker rewriting the page
0.55Blockers remove elements, cancel requests and inject stylesheet rules at document start. The challenge script sees a page whose own resources did not arrive and whose DOM does not match what the server sent — which is also exactly what a scraper's page looks like.
We think this one is probably wrong
Probably too high for the mainstream blockers, which most vendors have long since learned to tolerate. It is more defensible for aggressive custom filter lists. -
Strict anti-fingerprinting settings
0.50Resist-fingerprinting modes pin the time zone to UTC, round the screen, clamp the core count and flatten the pixel ratio. The values are common; the *combination* is not, and it is a combination almost nobody arrives at by accident.
-
A browser build well behind current
0.45Automation stacks pin their browser versions and update late, so version lag correlates with automation from the vendor's side. It also describes a great many ordinary people on managed machines, which is the whole complaint this site exists to answer.
-
Blocked third-party storage
0.45Several challenge flows carry their pass across a second origin. When that write is refused, the pass cannot be read back and the challenge is re-served — which is what a captcha loop looks like from the inside.
-
An unusual installed font set
0.40Too few detected families is a hardened or containerised profile. Too many is a design or development machine. Both sit outside the band a stock operating system produces, and the low end matters far more than the high end.
-
Desktop Chrome reporting no plugins
0.35Every shipping desktop Chrome reports a small fixed plugin and MIME list. An empty one is a long-standing headless tell that costs nothing to check.
-
Coarsened or unusual timer resolution
0.35A performance.now() that only moves in large steps is a hardening feature, and a challenge that measures its own execution can see it. Weak on its own; it mostly matters as corroboration.
We think this one is probably wrong
Probably close to zero in practice. Kept on the list because it is cheap to read and because cross-origin isolation changes it for reasons that have nothing to do with the visitor. -
Request headers disagree with the page
0.80The headers arrive with the request and the page values arrive with the challenge script, so a check holds both for free. A browser does not disagree with itself; an extension that rewrites one side does, and checks treat that as a rewritten value.
-
Input events not made by a device
0.70Browsers mark every event a script creates as untrusted, and invisible challenges read that mark along with how the pointer moves. A click no hand made is the plainest behaviour signal there is.
We think this one is probably wrong
Probably too low for scripted events and too high for the constant-speed test, which a steady trackpad swipe can come close to. -
A connection the browser did not make itself
0.50A current browser negotiates HTTP/2 or HTTP/3 over TLS 1.3 with any large edge network. An older protocol under a current browser's name means a proxy, a scanner or a script made the connection, and the edge sees that before any page code runs.
We think this one is probably wrong
Unknown in practice. Edge networks see the connection fingerprint directly and almost certainly weigh it more finely than this one number.
The omissions are as much the method as the table is. Each of these is a thing a page like this is expected to show, and each one is missing on purpose.
- How rare you are among real people
- It would need a representative sample of the web's browsers. These sites count the browsers that visit them, and a row shows that count, but visitors to fingerprint sites are not the web, so no percentile is made from it.
- Whether a particular vendor would actually challenge you
- That would need challenge outcomes from real vendors, labelled, at volume. No such dataset is public. A page that shows you a vendor's logo beside a pass or fail is inventing the result.
- Which extension you have installed
- Detection here is by side effect only — what happened to this page — and never by asking whether a particular extension identifier resolves. Probing you for a list of your extensions to tell you that you are being probed would be an unusual way to run this site.
- Anything about the pages you visit elsewhere
- This site records visits to its own pages and nothing else. It has no history access and no account to attach one to.
- A single number for how automated you look
- A score implies a scale and a population that is the web, which nobody here has. The reading gives ranked rows and a word, and the word is the name of a cause rather than a verdict.
A method that cannot be falsified is not a method. Three things would settle most of this table, and none of them require anything from us:
- Run the same request twice with one variable changed. Take a site that challenges you, connect through a VPN exit in your own country, and try again. If the challenge behaves identically, the geography weight above is too high and it should be lowered.
- Publish challenge outcomes. Any dataset of real challenge results, labelled by configuration, replaces the entire weight column with measurement. Until one exists this column is a judgment and is labelled as one on every row.
- Publish frequencies. A representative sample of browser values replaces the assumptions with counts, and the word “assumed” comes off the band at the bottom of every page.
Both tables are in the source, in one file each, in the same shape they render here. Corrections are welcome and get an entry in the changelog with a date on it. The site's own code and the browser it recommends are both public: github.com/zhom/donutbrowser.
Why the two halves are kept apart
They fail for different reasons and they will be corrected by different evidence, so combining them into one tuned number would make both unfixable. An assumption about how common a value is goes stale on its own as the web changes underneath it — a browser version threshold written today is wrong in a year with nobody touching it. A weight is wrong differently: it is wrong when a vendor changes what it acts on, which happens without announcement and is only visible in the challenges themselves.
Keeping them separate means a correction lands in exactly one place. When somebody publishes frequencies, the assumptions are replaced wholesale and the weights are untouched. When challenge outcomes become available, the reverse. Neither correction requires re-deriving the other, and neither can be hidden inside the other.
What a reading is not
It is not a verdict, and the design refuses to imply one: no badge, no percentage, no green or red. It is not a prediction about any specific site, because that would need the vendor's thresholds and those are not public. And its rank is not a comparison against other people: the counts from other browsers sit beside a row, not inside its rank.
What it is: a list of things about your setup, ordered by an arithmetic you can check, each one saying what an ordinary browser shows in the same place and what you could do about the difference. That is a narrower offer than the category makes and it is one that can be kept.
What people ask about a published method
- How does this site decide which of my settings to blame first?
- One multiplication, and both halves are printed on the row. Surprisal is how unexpected your value is under a written assumption; the challenge weight is a published judgment of how hard a bot check acts on that signal. Multiply them and you have the row's rank. There is no model, no hidden term and no per-visitor tuning — you can redo any row's arithmetic on a calculator.
- Are these weights based on real bot detection test results?
- No, and it would be easy and dishonest to imply otherwise. They are formed from published vendor documentation and from the observable behaviour of the challenges themselves. No dataset of labelled challenge outcomes is public, so nobody outside those vendors can weight these signals from evidence. Every number is marked as a judgment, and several are marked as probably wrong.
- Why won’t this site give me a bot score out of 100?
- A score implies a scale and a population to place you on, and the population these sites have is small and not the web. What you get instead is ranked rows in your own values, each showing the two numbers it was computed from, and a word — the name of your top cause — rather than a figure. That is less satisfying and considerably more useful, because you can act on a cause.
- Can I check whether this method is wrong?
- Yes, and the page says exactly how. Take a site that challenges you, change one variable — connect through an exit in your own country, say — and try again. If nothing changes, the geography weight here is too high and should come down. Both tables live in the source in the same shape they render, and a correction gets a dated changelog entry whether or not it came from us.
- Does this site know whether Cloudflare or reCAPTCHA would block me?
- No, and it refuses to guess. That would need labelled challenge outcomes from real vendors at volume, and no such dataset is public. A page that shows you a vendor's name beside a pass or a fail is inventing the result. What this site can honestly tell you is what a check would read about your setup, and which parts of that you can change.
The rows you cannot fix from a settings page
A VPN you need for work, an address your ISP shares with a thousand other subscribers, a laptop with no graphics chip, a hardened profile that breaks the sites it was meant to protect — none of these have a checkbox. What is left is to present a reading that is internally consistent and ordinary on purpose, instead of one that is accidentally strange.
Donut Browser does that at the engine level: its Wayfern Chromium fork applies the fingerprint through CDP injection rather than patching JavaScript in the page, and it refuses to launch a profile whose proxy exit country, time zone and language contradict the profile's own fingerprint — which is the top row of most readings on this site. It is AGPL-3.0, it has no telemetry of any kind, and it runs on macOS, Windows and Linux.
Stated plainly: this site has no test results showing what any particular anti-bot service does with any particular browser, and neither does Donut Browser's own repository. What can be checked is the source, which is public.
Free · AGPL-3.0 · github.com/zhom/donutbrowser