Privacy Tools Guide

ETags (Entity Tags) are HTTP headers designed to improve web performance through conditional caching. However, these same mechanisms create privacy concerns because they can function as persistent identifiers. Understanding how ETag tracking works helps developers build more privacy-respecting applications and enables users to protect their digital footprint.

What Are ETags and How Do They Work?

When a browser requests a resource from a server, the server can include an ETag header in its response. The ETag is a unique identifier for a specific version of that resource, typically generated as a hash or checksum:

HTTP/1.1 200 OK
Content-Type: text/html
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

On subsequent requests for the same resource, the browser sends the ETag back using the If-None-Match header:

GET /style.css HTTP/1.1
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

The server compares this value against the current version. If unchanged, it returns 304 Not Modified without retransmitting the body, saving bandwidth and reducing latency. This mechanism is fundamental to efficient web caching.

How ETag Tracking Occurs

The tracking potential emerges from how ETags interact with browser caching and third-party requests. Several scenarios enable this:

1. First-Party ETag Tracking

Servers can embed unique identifiers directly into ETag values. When users revisit a site, the browser’s cached ETag gets sent back, allowing the server to recognize the returning visitor:

# Server generates unique ETag for this user
ETag: "user-8f3a2b1c-d4e5-6789-abcd-ef0123456789"

Combined with authentication or account systems, this creates persistent user profiles. Even users who clear cookies can be tracked if they maintain the same browser instance.

2. Third-Party ETag Injection

Third-party trackers embedded across multiple sites can exploit ETags for cross-site tracking. A tracker present on hundreds of websites can assign the same ETag identifier to a user and read it back when that user visits any participating site:

// Third-party tracker sets a unique ETag
// On example-site.com
fetch('/tracker-pixel', {
  method: 'GET',
  headers: {
    'If-None-Match': 'tracker-id-ABC123'
  }
});

// On another-site.com - same ETag sent
fetch('/tracker-pixel', {
  method: 'GET',
  headers: {
    'If-None-Match': 'tracker-id-ABC123'
  }
});

This technique predates and complements cookie-based tracking, working even when users block third-party cookies.

3. ETag Respawning

Some tracking systems deliberately use ETags to “respawn” deleted cookies. When a cookie is removed, the ETag mechanism can recreate the tracking identifier on subsequent requests, defeating cookie deletion attempts.

Detecting and Analyzing ETag Tracking

Developers can examine ETag behavior using browser DevTools:

  1. Open Network tab in Chrome/Firefox DevTools
  2. Reload a page and locate responses with ETag headers
  3. Check if the same ETag persists across sessions
  4. Use Incognito mode to compare ETag values

For deeper analysis, examine HTTP traffic:

# Using curl to inspect ETag headers
curl -I https://example.com/page

# Check if ETag changes or persists
curl -H "If-None-Match: \"test-etag\"" -I https://example.com/page

Code Examples: Working with ETags

Server-Side Implementation (Node.js/Express)

const express = require('express');
const crypto = require('crypto');
const app = express();

app.get('/resource', (req, res) => {
  const content = generateContent();
  const etag = crypto.createHash('md5').update(content).digest('hex');

  // Check if client has matching ETag
  if (req.headers['if-none-match'] === etag) {
    return res.status(304).end();
  }

  res.set('ETag', `"${etag}"`);
  res.send(content);
});

Handling ETags Responsibly

To avoid unintended tracking while maintaining caching benefits:

app.get('/resource', (req, res) => {
  const content = generateContent();
  // Use content-based ETags, not user identifiers
  const etag = crypto.createHash('sha256').update(content).digest('hex');

  res.set('ETag', `"${etag}"`);

  // For privacy-sensitive resources, use short cache
  // or disable ETags entirely
  if (req.query.private) {
    res.set('Cache-Control', 'no-store');
    return res.send(content);
  }

  res.send(content);
});

Client-Side: Controlling ETag Behavior

Users can influence ETag behavior through browser settings:

// Service Worker: intercept requests to modify caching
self.addEventListener('fetch', event => {
  // Remove ETag headers from sensitive requests
  const request = new Request(event.request, {
    headers: { ...event.request.headers, 'If-None-Match': undefined }
  });
  event.respondWith(fetch(request));
});

Mitigating ETag Tracking

For Users

For Developers

  1. Avoid user-specific ETags: Use content hashes rather than session identifiers
  2. Set appropriate cache headers: Use Cache-Control directives to limit persistence
  3. Strip third-party ETags: Remove ETag headers from third-party resource responses
  4. Implement privacy-preserving caching: Consider using service workers with privacy-aware caching strategies
// Set privacy-conscious caching headers
res.set({
  'ETag': contentHash,           // Content-based, not user-based
  'Cache-Control': 'max-age=3600, must-revalidate',
  'Vary': 'Accept-Encoding'      # Don't cache per-user variants
});

The Broader Tracking Ecosystem

ETags represent one piece of a larger tracking infrastructure that includes cookies, fingerprinting, and various HTTP headers. While browser vendors and privacy regulations push toward reduced tracking, understanding these mechanisms remains essential for both privacy-conscious users and developers building respectful applications.

Modern browsers increasingly implement protections against ETag-based tracking. However, the arms race between privacy tools and tracking technologies continues. Staying informed about these mechanisms helps users make better decisions about their browsing habits and enables developers to build more privacy-respecting systems.

The key takeaway: ETags are a legitimate caching optimization that becomes a tracking vector when servers choose to use them as persistent identifiers. Awareness and appropriate configuration on both sides can significantly reduce unnecessary tracking while maintaining the performance benefits of HTTP caching.


Built by theluckystrike — More at zovo.one