Font fingerprinting: 130 of 187 fonts drawn but not listed

Font fingerprinting compares the fonts a browser lists with the fonts it draws. Our persona list left 130 of 187 families unlisted; voices follow the same rule.

Davide Grasböck · Founder, Scalebrowser

Published Oct 14, 2026 · 6 min read

Short answer

Font fingerprinting compares the fonts a browser lists with the fonts it can draw. When one of our builds served a persona's font list, it listed 59 families while the Windows machine had 187, and 130 of those 187 still rendered without being listed, a contradiction a single comparison finds. Speech voices obey the same rule over time: real Chrome on Windows reports 0 voices at page load and then 40, of which 21 are local and 19 are Google network voices. Since the 154 engine, measured on 14 September 2026, our browser lists all 187 families like real Chrome.
130 of 187, drawn but not listed, beside a stack of six thin plates

A font list looks like the safest thing to spoof: it is only a list, and no page can open the machine's font folder to check it. That reasoning cost us a build. The page cannot open the folder, but it can ask the browser to draw text, and the browser draws from the fonts the machine really has. On 12 September 2026 we put the listed fonts and the drawn fonts of four browsers side by side on one Windows machine, and one of the four contradicted itself on 130 families.

What is font fingerprinting?

Font fingerprinting is the reading of which fonts a machine has, through two separate surfaces: the list a page can enumerate and the text it can measure. Enumeration goes through the Local Font Access API, whose queryLocalFonts() returns every installed font face, but only after a user gesture and a local-fonts permission, according to MDN's reference. Measurement needs neither. A script renders the same string once in a named family with a generic fallback and once in the fallback alone; if the width changes, the family is installed.

Measurement is also the richer reading. David Fifield and Serge Egelman at UC Berkeley measured the on-screen size of rendered glyphs in 1,016 browsers and found font metrics more telling than the user agent string.

Reading over 1,016 browsersEntropyBrowsers identified uniquely
User-Agent string5.148 bits116 (11 percent)
Font metrics7.599 bits349 (34 percent)

The study also found that 43 of over 125,000 Unicode code points were enough to account for all the variation it saw. A fingerprinting script does not need to be slow or visible to read the fonts; it needs a few dozen characters drawn off screen.

What did a persona font list get wrong?

A persona font list got the source wrong: it answered the enumeration from the persona while Windows drew the text from the machine's own fonts. On 12 September 2026 we granted the font permission in four browsers on the same Windows machine, enumerated with queryLocalFonts(), and then tested every family the machine had with the measurement method above.

BrowserFamilies listedFaces listedDrawable but not listed
our 151 engine with a persona font list59203130 of 187
real Chrome 153, the machine's own list1874490
Source: Scalebrowser engine axes bench, one Windows 11 machine, same page and permission in every browser, 12 September 2026

The 130 families that rendered without being listed included Cascadia Code, Agency FB, Algerian, Arial Rounded MT and Baskerville Old Face. A page with the permission sees a machine that claims one font cupboard and draws from another, in one comparison and without any ambiguity. Chrome for Testing 151 and the same engine without a persona both listed the full 187, so the defect sat in the persona list alone.

We withdrew the list. On the 154 engine, measured on 14 September 2026 and shipped on 20 September, our browser lists the same 187 families and 449 faces as real Chrome 153 and Chrome for Testing 154, and 0 of 187 render without being listed. One family, Gill Sans, is listed but does not render, in all three browsers alike: a property of that machine's font files, which is exactly the kind of quirk a real machine has and an invented list does not.

The permission prompt means few sites read the list at all. That is no defence: a list that only holds up while nobody asks is still a claim the machine can contradict, and a detector that does ask gets a certain answer.

Why do speech voices belong to the same question?

Speech voices belong to the same question because speechSynthesis.getVoices() is another list of what the machine and the browser brand provide, and it has to agree with both. It adds a second dimension, time: the list is empty when the page loads and fills in once the browser fires the voiceschanged event. A single reading therefore answers nothing, and our bench reads the list 17 times, once per second; the table shows the run of 14 September 2026.

Browser on the same Windows machineAt page loadAfter voiceschangedLocalGoogle networkMarked default
real Chrome 15304021191
Chrome for Testing 1540212101
our 154 engine with a persona04021191

The first voiceschanged arrived between 1 and 29 milliseconds after load in all three, and no list grew or shrank after that within 16 seconds. Branded Chrome adds 19 network voices to the 21 the operating system provides; an unbranded build such as Chrome for Testing has only the local ones. Two earlier builds of ours broke this: one handed out a full list at page load, and one marked two voices as default where every control marked exactly one. Neither survives in the engine shipped on 20 September 2026. The voice list as a fingerprint surface is covered in speech synthesis voices.

Fonts and voices are two of the surfaces covered in what a fingerprint reads, and they share one rule: the list is the machine's, so the only safe list is the machine's own. A probe that stops at the first voiceschanged sees half the story, which is how we twice misread our own 151 build as defective when it was not. Another surface, the audio stack, is covered in audio fingerprinting.

Does the font list have to match the user agent's OS?

Yes. The fonts have to belong to the operating system the browser claims, and Windows 11 is recognisable by its fonts alone. Microsoft's Segoe Fluent Icons page states that the font "comes with Windows" 11 and "is not included by default on Windows 10". In our run of 14 September 2026 on a Windows 11 machine, all four browsers listed and drew it, and all four sent a Sec-CH-UA-Platform-Version of 19.0.0, which Microsoft maps to Windows 11 from 13.0.0 upward.

A browser that claims macOS while drawing Segoe UI, or Windows 10 while drawing Segoe Fluent Icons, describes no machine. The platform, the version and the client hints come from the same claim as the user agent and browser version, which is why a profile only starts on the operating system its identity names, as the coherence page explains. The checks a site runs on the driver rather than on the machine are covered in navigator.webdriver and CDP detection.

Run it on your own machine

Seven days to try it with your own agents on your own sites. Starting the trial needs a card.

Written by

Davide Grasböck

Founder, Scalebrowser

Builds Scalebrowser, the browser layer for AI agents that runs on your own machine. Measures every change a web page could observe against a real browser before it ships, and writes up the ones that turned out wrong.

All articles by Davide Grasböck