Skip to content
Anti-Bot

What Does a Bot-Check Widget Collect? About 40 Probes in Two Seconds

Pim

Pim · Scrappey Research

October 28, 2026 4 min read

What Does a Bot-Check Widget Collect? About 40 Probes in Two Seconds — conceptual illustration
On this page

A bot-check widget is the small frame a site embeds to decide whether a visitor gets an automatic pass, a checkbox or a harder challenge, and before it decides it runs dozens of probes against the browser. Public reverse-engineering of one widely deployed widget in October 2026 counted 38 probes per session, covering the property surface, native functions, rendering, fonts and locale, network and CPU, plus a summary of the host page sent by the widget's loader. Most probes do not look for rare values. They check whether the browser's answers agree with each other.

Quick facts

Probes per session38 in one widely deployed widget (Oct 2026)
Device facts23 of the 38: constant for a given machine (surface, rendering, fonts, Intl)
Session valuesTimings, WebRTC candidates, proof of work, requests made now
Runs insideA cross-origin iframe with an obfuscated, per-session virtual machine
Also reportedHost page summary: element counts, form type, render stack, visibility

How the widget runs

The widget loads in a cross-origin iframe, so the page that embeds it cannot read or patch what runs inside. Its script is obfuscated, and the collection code is bytecode for a small register machine whose opcodes and handler order change per session. Field names are randomised per build and records are shuffled per session. What cannot be renamed are the browser APIs each probe calls, such as getParameter or getHighEntropyValues, which is why public analysis can still map what is collected.

The probes group by the question they ask:

GroupExamples
Property surfacewindow, navigator, document and screen, grouped by value type, read from a fresh iframe
Native inspectionAbout 150 built-ins converted to strings and timed per call
Automation presenceA console stack getter, a debugger statement in a worker, Node-style globals
RenderingWebGL 1 and 2 parameters and extensions, WebGPU adapter info and limits, canvas, OfflineAudioContext, SVG text and element boxes
Host and localeA 52-font list, a flag emoji, Intl strings, keyboard layout, client hints, historical timezone offsets
NetworkCodec support, WebRTC ICE gathering, a worker fetch, resource timing, a WebSocket echo
ComputeWebAssembly kernels, the performance.now() step, a timed SHA-256 proof of work

What the page around the widget reports

The iframe cannot read the host page, so it asks the widget's loader script, which runs in the page, and the loader answers with a summary. In the analysed widget that answer contains:

  • Configuration and timing: the widget settings, the loader's own resource timing, and six elapsed-time measurements from loader start to the iframe's first message.
  • A render stack: new Error().stack taken while the widget was rendered, which shows whether it came from the page's markup or from a script calling the render API; see stack trace inspection.
  • The page shape: counts of elements, scripts, stylesheets and meta tags, a short DOM-shape string, the widget's XPath and position, the title's length and hash, the referrer and document.visibilityState.
  • The form: method, action origin, control counts and a keyword guess at its purpose (login, signup, checkout, contact and others).

A widget on a real login page sits in a form, on a page with dozens of elements, a title and a referrer, in a visible tab. A bare test page that loads only the widget has a handful of elements and no form, and that difference travels with the first payload.

Device facts, session values and noise

Twenty-three of the 38 probes read device facts: answers a given machine gives every time. These cannot be expected to be unique, because millions of phones of one model return the same Math output, Intl strings and WebGL parameters. What they can be checked for is possibility: do these WebGL limits exist on a GPU that fits the claimed platform, do these fonts belong to the claimed OS, does the clock step fit the claimed browser. The rest are session values such as timings, ICE candidates and the proof of work, which show the answers were produced now by a browser that really made the requests.

Noise is part of the picture. One major browser adds its own noise to canvas and audio readbacks, so its hashes change per session, while stock desktop Chrome returns identical canvas, WebGL and audio answers on one machine. Stability is a property of the claimed browser: random noise on an engine that never adds any is as inconsistent as a stable hash on one that always does.

Taken together the list is a set of questions that only make sense together: the property surface has to fit the User-Agent, WebGPU has to fit WebGL, fonts and emoji have to fit the platform, Intl and timezone history have to fit the language and the IP. That is the coherence problem a managed web data API solves by running real browsers on matching hardware and network, rather than by answering each probe separately.

Code example

javascript
// A few of the same questions, asked from your own browser console.
// Each value is ordinary; what matters is whether they agree.
const gl = document.createElement('canvas').getContext('webgl');
const dbg = gl.getExtension('WEBGL_debug_renderer_info');
const hints = await navigator.userAgentData?.getHighEntropyValues(
  ['platform', 'platformVersion', 'architecture', 'fullVersionList']);

console.log({
  ua: navigator.userAgent,
  platform: hints?.platform,
  renderer: dbg && gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL),
  cores: navigator.hardwareConcurrency,
  memory: navigator.deviceMemory,
  heapLimit: performance.memory?.jsHeapSizeLimit,
  timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
  languages: navigator.languages,
  compact: new Intl.NumberFormat(undefined, { notation: 'compact' }).format(2.1e16),
  visibility: document.visibilityState,
});
// A widget asks the same things (and about 30 more) and checks, for example,
// that the renderer fits the platform and the Intl output fits the languages.

Related terms

Concept map

Concept map

How What Does a Bot-Check Widget Collect connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Anti-Bot
Building map…

Frequently asked questions

Can a challenge widget read the page it is embedded in?

Not directly, because it runs in a cross-origin iframe. Its loader script runs in the page, though, and sends the iframe a summary: element counts, a DOM-shape string, the form type around the widget, the title hash, the referrer, visibility and a stack sample of the render call.

Does a challenge widget need my fingerprint to be unique?

No. Most device facts are shared by millions of identical devices. The widget checks that the answers are possible together, for example that the WebGL limits, fonts and clock step fit the claimed platform and browser.

Is it suspicious if my canvas hash changes between sessions?

It depends on the browser. One major browser adds noise to canvas and audio readbacks on purpose, so changing hashes are normal there, while stock Chrome returns the same values every time. The expected noise is part of what the claimed browser predicts.

Last updated: 2026-10-28 · Facts last verified: 2026-10-28