A navigator.userAgentData sensor: what one inconsistency says about a visitor
Written by Claude (AI) · research signals and editing by Nathan Pankowsky · see AI usage at the end
On three small sites, 1,409 visitors used a desktop Chromium browser that answered navigator.userAgentData. For 882 of them, one value in that answer didn't fit the device the browser claimed to be.
None of the 882 clicked an ad, reached a cart or contact page, viewed five pages, or came back another day. Of the visitors whose answer was consistent and who weren't on a datacenter address, 44% clicked an ad and 9% reached a goal page.
The check picked out one automated fleet almost perfectly. It missed a second fleet that got the value right, which a different check caught. One sensor finds one kind of bot.
What the sensor reads
navigator.userAgentData is the JavaScript side of User-Agent Client Hints. Chromium browsers (Chrome, Edge, Samsung Internet, Opera) have it; Safari and Firefox don't. It gives a page a few basic values straight away (brand, platform, whether the device is mobile) and more detailed ones on request through getHighEntropyValues() (MDN).
A real browser's answers agree with each other and with its user-agent string. Tools that disguise an automated browser often change the user-agent string and leave this API behind, or fill it in only partly. That mismatch is a known tell (send.win).
Our sensor compares one value in this answer with the device the browser says it is. We don't say which value. We found no public discussion of it as a bot tell, and naming it would tell the tools what to fix. We share the details with customers and researchers on request.
Our data
The same three sites as our previous posts, over two to four days in October 2026. We withhold their names.
- A visitor is an IP address. Verified crawlers and self-declared bots are left out.
- Desktop Chromium visitors are those whose user-agent string names Windows, macOS, Linux or ChromeOS and whose browser answered
navigator.userAgentDataon at least one page view: 1,409 in all. Phones are left out of this post. - Fail means at least one of the visitor's readings failed the check. Pass means none did.
- The comparison group is passing visitors who were not on a datacenter address (ipinfo's flag), 281 of them. Passing visitors on datacenter addresses are a story of their own (below).
- Goals are the store's payment pages, the lettings site's contact page and the shop's cart. They are page views, not confirmed orders or enquiries.
Result 1: one site, nearly all of it
| Site | Desktop Chromium visitors | Fail | Pass |
|---|---|---|---|
| Israeli e-commerce store | 86 | 0 | 86 |
| UK lettings site | 145 | 5 | 140 |
| UK jewelry-packaging shop | 1,178 | 877 | 301 |
The failures sit almost entirely on the shop. On the store, which gets few desktop visitors and fewer datacenter ones, nothing failed. That is one site's bot traffic, not a rate you should expect on yours.
Browser version doesn't explain it. All but one failing visitor claimed Chrome 142 to 148, while most passing ones ran 154 or 155, the current versions. But 128 other visitors on Chrome 148 passed, so the version alone doesn't produce the failure.
Result 2: who fails
| Fail (882) | Pass (527) | |
|---|---|---|
| On a datacenter address | 94% | 47% |
| On a home or mobile line ipinfo flags as a residential proxy | 0% | 9% |
| Clock doesn't fit the IP's country | 96% | 41% |
| Clock set to New York | 98% | 1% |
| Language set to US English | 76% | 42% |
| Claims to be a Mac | 56% | 7% |
| Browser older than version 150 | 100% | 28% |
All differences have p < 0.001 (Fisher's exact test).
The failing visitors look like one fleet. They ran from a handful of hosting providers (M247, HostRoyale, Mod Mission Critical, SYN) and two networks ipinfo doesn't mark as hosting (a Lithuanian telecom and a backbone carrier). Nearly all had New York clocks, on addresses mostly in France, the Netherlands, Spain, India and Poland. This is the same population that dominated the clock mismatches in our previous post.
Result 3: intent and goals

Chart: visitors who failed the check never clicked an ad or reached a goal. Share of desktop Chromium visitors; failed the check (882) against passed and not on a datacenter address (281). Source: reproduce_uad.py, three sites, 9–14 Oct 2026.
| Failed the check (882) | Passed, not datacenter (281) | |
|---|---|---|
| Clicked an ad | 0% | 44% |
| Reached the goal page | 0% | 9% |
| Viewed five or more pages | 0% | 23% |
| Stayed more than a minute | 1% | 37% |
| Came back another day | 0% | 7% |
| Viewed exactly two pages | 88% | 17% |
The failing visitors browsed in one pattern: 88% viewed exactly two pages, usually the home page and a collection, and left within the minute. None of them clicked an ad, reached a goal page or returned. Every difference in the chart has p < 0.001.
They did reach product and collection pages more often than people did (95% against 90%). On this shop, a visit to a product page is not a sign of interest. It is what a catalogue scraper does.
The datacenter addresses don't explain this on their own. The 55 failing visitors on networks ipinfo doesn't flag as hosting browsed the same way: 73% viewed exactly two pages, none stayed more than a minute, and none clicked an ad or reached a goal. Among passing visitors on the same kind of network, 44% clicked an ad.
For advertisers, the result is reassuring rather than alarming. All 131 desktop Chromium visitors who clicked an ad passed the check. The fleet it catches looks like unpaid scraping, not click fraud.
What the check misses
246 passing visitors were on datacenter addresses, and most of them don't look like people either:
- 127 on a Chinese cloud provider's network, all claiming Chrome 148 on Windows, all with a Shanghai clock on addresses in the US, Singapore, South Korea and Hong Kong. Most viewed a single collection page. Their
navigator.userAgentDataanswer was consistent, so this check passed them. A clock-against-IP check catches them. - 85 Linux machines on Chrome 155 with a UTC clock. Almost all render graphics in software, and another of our checks flags them.
Of the 246, only 3% clicked an ad and 88% viewed a single page, against 44% and 48% for passing visitors elsewhere.
So the check is precise but narrow. Of the 882 visitors it flagged, none clicked an ad, reached a goal or came back. It also saw nothing of two other fleets on the same site. A fleet that fixes this one value will pass it too, which is why we don't name the value.
Limits
- One site, one fleet. 877 of the 882 failures are on the shop, and they look like one operation. On the other two sites the check barely fired. We can't say how common this is elsewhere.
- Short windows. Two to four days per site.
- No ground truth. "Never clicked an ad, never reached a goal" describes the visitors; it doesn't prove they were automated.
- Goals are page views, not confirmed orders or enquiries.
- Desktop Chromium only. Safari and Firefox don't have
navigator.userAgentData, so the check says nothing about their visitors. Phones are left out. - It is easy to fix. A tool that fills in the value correctly passes. We expect this fleet to do so eventually.
- The value is withheld, so you can't reproduce the check itself, only our counts from the pass/fail column we publish.
What to do with it
- Read
navigator.userAgentDataalongside the user-agent string, not instead of it. The useful signal is disagreement between the two, not either one alone. - Don't use it to judge paid clicks on its own. Here it flagged no ad clicks at all. Its value is in clearing scraper traffic out of your analytics, so that bounce rates, page counts and audience sizes describe people.
- Pair it with checks that look elsewhere. On the same site, the device clock against the IP's country caught a fleet this check missed, and a graphics check caught another. No single sensor saw all three.
- Expect it to stop working. Each tell lasts until the tool behind the fleet is fixed. Keeping the value private buys time; it doesn't buy permanence.
Try it on your own traffic
Button Solutions runs this check, among others, on the page views it monitors. Sign up.
Reproduce it yourself
The scrubbed data and scripts are on GitHub, shared with our previous posts: ip-quality-reproduction-oct-14-2026. For this post the data carries a pass/fail column for the check, the browser's major version and the operating system its user-agent string names, but not the value we check. reproduce_uad.py recomputes every figure here from it. Site names, IPs and click IDs are replaced with random tokens or removed.
The data and scripts are licensed CC BY-NC-ND 4.0: you may share them unchanged for non-commercial purposes, with credit to Button Solutions. If you reproduce or build on these figures, please cite this post and the dataset.
AI usage
This post was researched and written by Claude (Anthropic), with research signals and editing by Nathan Pankowsky. Claude wrote the analysis code and the text; every figure comes from that code.
References
- MDN, NavigatorUAData: getHighEntropyValues().
- send.win, User-Agent Client Hints detection.