Font Fingerprinting: How Your Fonts Identify You

Font fingerprinting uses CanvasRenderingContext2D.measureText() to detect installed fonts without canvas exports. How font sets identify OS and software, and how Brave and Firefox block it.

ZERO UPLOAD · ALL LOCAL
  1. The scan runs automatically when you open this page — no input needed.
  2. Each section shows a group of signals your browser exposes. Rows marked TRACKABLE are the highest-entropy signals used to identify you.
  3. The entropy score at the top estimates how uniquely identifiable your browser is across the web.
  4. Use the Copy buttons next to any row to copy that value to your clipboard.
  5. The Installed Fonts section lists which fonts from a 30-font probe were detected on your system.

What to look for

  • under 100ms
  • under 30ms
  • 30 to 50, per W3C guidance
Scanning browser APIs…
~0 bits

Analysing fingerprint…

27+ bits VERY HIGH
19 – 26 bits HIGH
9 – 18 bits MODERATE
0 – 8 bits LOW

IDENTITY

USER AGENTTRACKABLE
APP VERSION
PLATFORM
VENDOR
WEBDRIVER

HARDWARE

CPU THREADS
DEVICE MEMORY
SCREEN RESOLUTIONTRACKABLE
AVAILABLE SCREEN
COLOUR DEPTH
PIXEL DEPTH
PIXEL RATIO
ORIENTATION
TOUCH POINTS
BATTERY LEVEL
BATTERY CHARGING

GRAPHICS

CANVAS HASHTRACKABLE
WEBGL VENDOR
WEBGL RENDERERTRACKABLE
WEBGL EXT COUNT
WEBGL EXTENSIONS
MAX TEXTURE SIZE
WEBGL 2
WEBGPU

AUDIO

AUDIO FINGERPRINTTRACKABLE
SAMPLE RATE
BASE LATENCY

NETWORK & PRIVACY

ONLINE
DO NOT TRACK
COOKIES ENABLED
CONNECTION TYPE
DOWNLINK
RTT
SAVE DATA

BROWSER ENVIRONMENT

LANGUAGE
LANGUAGESTRACKABLE
TIMEZONETRACKABLE
TIMEZONE OFFSET
PLUGIN COUNT
PLUGINS
LOCAL STORAGE
SESSION STORAGE
INDEXEDDB
WEBSQL
CODEC H.264
CODEC VP8
CODEC VP9
CODEC AV1
CODEC HEVC
SPEECH VOICES
VOICE NAMES

SYSTEM PREFERENCES

COLOUR SCHEME
REDUCED MOTION
CONTRAST
POINTER
HOVER
FORCED COLOURS

INSTALLED FONTS

INSTALLED FONTSTRACKABLE
Show detected fonts (30 probed)

Font Fingerprinting: How measureText() Detects Your Installed Fonts

For privacy-sensitive pages, font fingerprinting detects which fonts are installed on your system. A script renders a test string in a probe font using CanvasRenderingContext2D.measureText() and compares the measured pixel width to the fallback font width. When the measured width matches the fallback, the probe font is absent; when the width differs, the probe font is installed. This detection method runs silently in any browser context and requires no canvas image export. The W3C fingerprinting guidance specifically cites font enumeration as a high-risk fingerprinting vector because the installed font set reflects the OS, locale, installed software portfolio, and even professional specialization of the user.1 Consequently, an Illustrator user has Adobe Fonts; a developer has JetBrains Mono and Fira Code; a gamer may have specialized peripheral vendor fonts.

Building on this, a script probing 200 fonts can enumerate the installed set completely in under 100 milliseconds, making it one of the fastest high-entropy fingerprinting techniques available.2

How canvas-based font probing works

Font probe detection uses CanvasRenderingContext2D.measureText() rather than canvas pixel export. The script creates a canvas, obtains a 2D context, sets a specific font size, and renders a test string like "mmmmmmmmmmlli" in each probe font.3 It calls measureText() on the test string and records the returned pixel width. The same test string in the fallback font (typically "monospace" or "sans-serif") has a known baseline width. When the probe font is installed, the measured width differs from the baseline because the font uses different glyph widths.

Why font probing is hard to notice

When the probe font is absent, the browser falls back to the default font and the measured width matches the baseline. Consequently, no canvas pixel buffer is exported; measureText() returns a number, not an image, which means canvas image blocking does not prevent font detection. Building on this, a script probing 100 fonts requires only 100 measureText() calls, completing in under 30 milliseconds on most hardware without triggering any visible browser behavior.

The speed is what makes font probing dangerous rather than merely possible. Each individual measurement finishes in a fraction of a millisecond, so a full scan of hundreds of fonts adds no perceptible delay to page load. You can watch the effect yourself in the font section of the fingerprint inspector, where the complete enumerated set appears instantly even though the script tested far more typefaces than a human could read.4

Why font sets identify OS, locale, and installed software

The installed font set is a deterministic function of three factors: the OS default installation, the locale settings, and the installed application portfolio. Windows 11 installs a specific set of Microsoft fonts absent from macOS. macOS installs a different set of Apple-licensed fonts, including several typefaces not present on Windows.5 Linux distributions vary widely, with some shipping only open-source fonts and others including commercial fonts under OEM licenses.

Beyond the OS defaults, locale settings add language-specific fonts: East Asian language packs include CJK typefaces not present in Western locale installations. Furthermore, application installations predictably add specific fonts: Adobe Creative Cloud adds Adobe Fonts, Microsoft 365 adds additional Office fonts, JetBrains IDE installs JetBrains Mono, and some gaming peripherals install vendor-specific display fonts. Building on this, the W3C fingerprinting guidance cites font enumeration as capable of identifying users across sites because the combination of OS default fonts plus application-specific fonts creates a distinctive profile that changes infrequently.1

Why font lists change so slowly

Installed fonts rarely change during a normal browsing session, so a font list remains stable enough for cross-session recognition. This stability is why browser-level font limiting is more effective than clearing cookies or site data. Unlike cookies, which users clear regularly, the font set persists across browser restarts, VPN changes, and even operating system updates, making it one of the most durable passive identifiers available to fingerprinting scripts. A font profile recorded during a first visit can re-identify the same browser months later with high confidence, even when all other session data has been reset.6

Blocking font fingerprinting without breaking text rendering

Blocking font fingerprinting requires restricting the font list that browsers report through canvas measurement, not blocking canvas image exports. Brave with Standard Shields active limits the font probing response to a common baseline subset, making all probe fonts outside the allowed list appear absent regardless of whether they are actually installed.7 Checking which fonts your browser exposes through measureText() reveals whether your Brave protection is actually working, since a protected browser returns a short consistent list rather than a detailed enumeration of every typeface you have installed.

How protections change font visibility

Firefox with privacy.resistFingerprinting enabled restricts font access through a similar mechanism, reporting a standardized font set rather than the actual installed set.8 Conversely, blocking canvas access entirely prevents measureText() from returning meaningful values but breaks font-dependent text layout applications including some CAPTCHA systems, online document editors, and text-rendering tools.8 The Brave approach provides the strongest protection with the smallest compatibility impact because it restricts the probe response rather than the API itself.

When to use this

Use this guide when investigating why the Installed Fonts section of CapyToolkit's Browser Fingerprint Inspector shows a distinctive result, or when evaluating whether your font set contributes significantly to your overall entropy score.

Examples

Font probe detection technique

Before
A script sets ctx.font = "72px monospace" and measures "mmmmmmmmmmlli", baseline width = 320px
After
The script then sets ctx.font = "72px 'Calibri', monospace" and measures again. If width differs from 320px, Calibri is installed. If identical, Calibri falls back to monospace, absent.

This single comparison runs in under 0.1ms. Repeating it for 200 fonts takes less than 30ms total, invisible in page load timing.

Font set in Brave vs. Chrome on the same Windows machine

Before
Chrome: full font list detected: Segoe UI, Calibri, Cambria, Adobe Fonts (if CC installed), JetBrains Mono (if IDE installed), etc.
After
Brave with Shields: reduced common subset returned: Segoe UI and common system fonts appear; application-specific fonts like Adobe Fonts and JetBrains Mono return as absent regardless of actual installation
Sources
  1. 1.

    W3C, "Mitigating Browser Fingerprinting in Web Specifications," w3.org, 2024. https://www.w3.org/TR/fingerprinting-guidance/

  2. 2.

    BrowserLeaks, "Font Fingerprinting," browserleaks.io, accessed July 2026. https://browserleaks.io/fingerprints/fonts

  3. 3.

    Mozilla Firefox, "Font Fingerprinter Test Page," github.com/mozilla-firefox, 2024. https://raw.githubusercontent.com/mozilla-firefox/firefox/main/browser/base/content/test/protectionsUI/font-fingerprinter.html

  4. 4.

    BotBrowser, "Text Measurement Fingerprinting: Sub-Pixel Tracking," botbrowser.io, 2024. https://botbrowser.io/en/blog/text-measurement-fingerprinting/

  5. 5.

    9to5Mac, "iOS 26 Will Counter One of the Web's Most Invasive Tracking Methods," 9to5mac.com, July 2025. https://9to5mac.com/2025/07/29/with-ios-26-safari-will-counter-one-of-the-webs-most-invasive-tracking-methods/

  6. 6.

    Mozilla Bugzilla, "Bug 1653987 — Restrict CSS font visibility to standard fonts only when privacy.resistFingerprinting is true," bugzilla.mozilla.org, 2021. https://bugzilla.mozilla.org/show_bug.cgi?id=1653987

  7. 7.

    Brave, "GitHub Issue #51331 — Font Fingerprinting Protection (2025)," github.com/brave/brave-browser, 2025. https://github.com/brave/brave-browser/issues/51331

  8. 8.

    Mozilla Firefox, "RFP breaks jigsaw-puzzle-type CAPTCHAs," bugzilla.mozilla.org, Bug 1747674. https://bugzilla.mozilla.org/show_bug.cgi?id=1747674

FAQ