Skip to content
All fingerprint checksRender & GPU

getBoundingClientRect === Range client rects

Check id bcr-vs-range

What an ordinary browser yields

Both APIs return the same left value, and both sit on the 1/512 grid.

What a detector infers

Two independent APIs are pointed at the same laid-out text: an absolutely-positioned div containing "WWWWWiiiii" is measured with element.getBoundingClientRect().left, and a Range selecting the same node contents is measured with getClientRects()[0].left. Both read out of the same layout tree, so on an unmodified browser they agree exactly and both land on the 1/512 grid — the check passes only when the two lefts agree within 1e-6 AND both are on-grid. That double condition catches two distinct situations: a hook that perturbs getBoundingClientRect (a common way to fuzz element geometry) rarely also covers Range client rects, so the readings contradict each other; and a scaled value fails the grid test even if both paths were covered. N/A if the probe could not produce both rects.

How to resolve it

Don't perturb rect geometry from JavaScript. If element geometry must vary, it has to vary in layout itself so every rect API reads from the same tree and stays quantised — otherwise the two readings of one element disagree, which no real rendering produces.

What is DOMRect fingerprinting?

Read in the wild by

CreepJS

CreepJS — an open-source research demo, not a production detector — registers both sides of this pair for tamper-testing in src/lies/index.ts: `searchLies(() => Range, { target: ['getBoundingClientRect','getClientRects'] })` and `searchLies(() => Element, …)` with both methods in its target list, alongside `searchLies(() => DOMRect)` and `searchLies(() => DOMRectReadOnly)`. What that code does is check whether those rect methods have been hooked or poisoned — it does not read the geometry or compare the two readings against each other; the cross-API comparison on this line is ours. The overlap worth noting is narrower: an independent research project arrived at the same two rect surfaces as the ones worth watching.

CreepJS src/lies/index.ts: `searchLies(() => Range, { target: ['getBoundingClientRect','getClientRects'] })`; `searchLies(() => Element, { target: ['append','appendChild','getBoundingClientRect','getClientRects','insertAdjacentElement', … ] })`; `searchLies(() => DOMRect)`; `searchLies(() => DOMRectReadOnly)`. Signature: `searchLies: (fn: Function, config?: SearchConfig): void` — tests prototype methods for tampering (toString tampering, getter poisoning, proxy obfuscation), not geometry values.

Every attribution traces to a published artifact. See the sources and their limits.

Nearby checks in Render & GPU

See all 32 checks in Render & GPU
Who builds this test

Clearcote is a browser built for fingerprint coherence

It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.

This audit takes no position on how Clearcote scores on Render & GPU checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.

See the other checks in Render & GPU — the family bcr-vs-range belongs to.