2026-04-09·14 min read·sota.io team

EU ePrivacy Directive & Cookie Consent: Developer Guide (2002/58/EC + 2017 Reform)

The EU ePrivacy Directive — Directive 2002/58/EC of the European Parliament and of the Council — is the lex specialis governing electronic communications privacy in the EU. It is not replaced by GDPR. It sits alongside GDPR as a separate, stricter regime for a specific category of data processing: cookies, tracking technologies, electronic communications metadata, and unsolicited marketing.

If you run analytics, A/B testing, session recording, advertising pixels, email marketing, or any tracking technology on a service with EU users, the eu eprivacy directive cookie consent developer guide is directly relevant to your legal exposure. The ePrivacy Directive is enforced by national Data Protection Authorities (DPAs) and national telecommunications regulators — enforcement is decentralized across 27 member states, creating significant divergence in practice.

This guide covers the current Directive 2002/58/EC, the 2017 Reform Proposal for an EU ePrivacy Regulation (COM/2017/0010), the intersection with GDPR, the TCF 2.0 framework, and the specific developer obligations for consent management, cookie APIs, and tracking infrastructure.

The ePrivacy Directive: Scope and Structure

What 2002/58/EC Actually Covers

The ePrivacy Directive (ePD) applies to the processing of personal data in connection with the provision of publicly available electronic communications services in the EU. Article 3 defines the scope:

Article 5(3) is the critical provision for developers:

"Member States shall ensure that the storing of information, or the gaining of access to information already stored, in the terminal equipment of a subscriber or user is only allowed on condition that the subscriber or user concerned has given his or her consent."

This is the cookie consent rule. It applies to any technology that stores or reads information on user devices — not just HTTP cookies, but also:

The exceptions to the consent requirement (Art. 5(3) second sentence) are narrow:

  1. Technical necessity for the transmission of communication (session routing)
  2. Strictly necessary for provision of an information society service explicitly requested by the user

"Analytics cookies" are not technically necessary. They serve the controller's interests, not the user's request. Multiple DPAs (FR CNIL, DE DSK, NL AP, BE APD) have confirmed this.

Confidentiality of Communications (Art. 5(1))

Article 5(1) prohibits listening, tapping, storage, interception, or surveillance of communications without consent of the users concerned. This applies to:

This is why SaaS email services processing EU users' emails must address both GDPR (as processor) and the ePrivacy Directive (confidentiality of content).

Traffic Data and Location Data (Art. 6-7)

Traffic data (data processed for transmission of communications) must be erased or anonymized after communication is complete, except for billing purposes (Art. 6(2)) or for specific law enforcement purposes.

Location data (other than traffic data) can only be processed with user consent or in anonymized form (Art. 9).

This creates obligations for:

PECR: The UK Implementation

The UK transposed the ePrivacy Directive as the Privacy and Electronic Communications Regulations 2003 (PECR), amended in 2011. After Brexit, PECR remains UK law and is enforced by the ICO (Information Commissioner's Office).

PECR applies to any organization targeting UK users with electronic marketing or setting cookies on UK user devices, regardless of where the organization is established. The substantive rules mirror the EU ePD closely, but enforcement patterns differ:

DimensionEU (2002/58/EC)UK PECR
EnforcerNational DPAs (27 member states)ICO
Cookie consentPrior consent, strict exceptionsPrior consent, same narrow exceptions
Soft opt-in (marketing)Not harmonized across member statesRegulation 22(3) — allowed for existing customers
Max fineVaries by member state (GDPR level in some)£500,000 (PECR-specific cap)
JurisdictionEU users or EU establishmentUK users or UK establishment

ePrivacy × GDPR Intersection

This is the most misunderstood area of EU privacy law for developers.

Dual Lawful Basis Requirement

GDPR establishes six lawful bases for processing personal data (Art. 6(1)):

The ePrivacy Directive's cookie consent rule is separate from GDPR Art. 6. But when cookies collect personal data (which analytics cookies usually do — they create unique identifiers linked to browsing history, IP addresses, etc.), both the ePD and GDPR apply simultaneously.

Cookie that collects personal data:
  1. ePrivacy Art. 5(3): requires prior consent (ePD)
  2. GDPR Art. 6: requires lawful basis (GDPR)
  3. GDPR Art. 13/14: requires privacy notice at collection time

All three requirements are independent.

The key practical consequence: legitimate interests (GDPR Art. 6(1)(f)) cannot substitute for the ePrivacy consent requirement. The Belgian DPA (APD) Databricks decision (2023) and the French CNIL Google Analytics decisions (2022) both confirmed this.

If you use Google Analytics, Microsoft Clarity, or any third-party analytics tool that sets cookies or fingerprints users, you need:

  1. ePrivacy prior consent (before any cookie is set)
  2. GDPR Art. 6(1)(a) consent as the lawful basis for data processing (must match ePD consent — one consent mechanism covers both)

For consent to be valid under both frameworks simultaneously (EDPB Guidelines 05/2020), it must be:

RequirementGDPR Art. 7ePrivacy Art. 5(3)
Freely givenYes — no bundling with serviceYes — equivalent standard
SpecificPer processing purposePer cookie category
InformedPrivacy notice requiredCookie disclosure required
Unambiguous indicationActive opt-inActive opt-in (no pre-ticked boxes)
WithdrawableAs easy as givingAs easy as giving
Prior to processingRequiredRequired ("before... storage or access")
DocumentedController burdenController burden

Pre-ticked boxes, scrolling, or continued browsing do NOT constitute valid consent under either regime. The CJEU Planet49 judgment (C-673/17, October 2019) confirmed this at EU level.

# Cookie categories and their legal status under EU ePrivacy

COOKIE_CATEGORIES = {
    "strictly_necessary": {
        "requires_consent": False,
        "examples": ["session token (JSESSIONID)", "csrf_token", "load balancer cookie"],
        "basis": "Art. 5(3) exception: strictly necessary for service the user requested",
        "gdpr_basis": "Art. 6(1)(b) contract — needed to provide the service",
    },
    "functional": {
        "requires_consent": True,  # Despite name, most "functional" cookies are not strictly necessary
        "examples": ["language preference", "dark mode", "font size"],
        "basis": "Prior consent required",
        "gdpr_basis": "Art. 6(1)(a) consent",
        "note": "Exception possible if user explicitly set the preference — arguable",
    },
    "analytics": {
        "requires_consent": True,
        "examples": ["Google Analytics (_ga, _gid)", "Plausible (cookieless — different analysis)", 
                     "Matomo (with cookie mode)", "Mixpanel", "Amplitude"],
        "basis": "Prior consent required — serves controller interest, not user request",
        "gdpr_basis": "Art. 6(1)(a) consent",
        "note": "Cookieless analytics (IP aggregation only, no cross-session ID) may fall outside ePD scope",
    },
    "advertising": {
        "requires_consent": True,
        "examples": ["Facebook Pixel", "Google Ads conversion", "LinkedIn Insight Tag",
                     "TikTok Pixel", "Meta CAPI server-side events (if linked to cookie ID)"],
        "basis": "Prior consent required",
        "gdpr_basis": "Art. 6(1)(a) consent",
        "note": "Server-side tracking that uses cookie-derived IDs is still in scope",
    },
    "session_recording": {
        "requires_consent": True,
        "examples": ["Hotjar", "FullStory", "LogRocket", "Microsoft Clarity", "MouseFlow"],
        "basis": "Prior consent required — captures keystrokes, clicks, forms (Art. 5(1) confidentiality + Art. 5(3) storage)",
        "gdpr_basis": "Art. 6(1)(a) consent — highly sensitive, DPA guidance often requires explicit notice",
        "note": "Session recording of form inputs may additionally trigger GDPR Art. 9 if health/financial data captured",
    },
}

The 2017 ePrivacy Reform Proposal (COM/2017/0010)

The European Commission published its Proposal for an ePrivacy Regulation on 10 January 2017 (COM/2017/0010). Unlike the Directive, this would be a Regulation — directly applicable in all member states without transposition, like GDPR. As of 2026, the proposal is still in trilogue (Commission–Council–Parliament negotiations), with no adoption date confirmed.

Key Changes in the 2017 Proposal

1. Extended Scope to OTT Communications

The current Directive applies only to traditional electronic communications providers (telecos). The Proposal extends scope to Over-the-Top (OTT) services — WhatsApp, Messenger, Signal, iMessage, Zoom, Teams. These services use the public internet rather than traditional telecom networks but functionally replace telephone and SMS.

This is the most controversial provision. Tech companies lobbied heavily against it. The Council's revised positions attempted to narrow the OTT definition. Final scope remains contested.

2. Browser-Level Consent Signals

Article 10 of the Proposal would require:

This maps to the Global Privacy Control (GPC) concept, which California's CPRA already recognizes as a valid opt-out signal. The ePR Art. 10 implementation would create an EU equivalent.

# Detecting and honoring browser consent signals
import re
from typing import Optional

def parse_consent_signals(request_headers: dict) -> dict:
    """
    Parse browser-level privacy consent signals from HTTP request headers.
    Relevant for EU ePrivacy Art. 10 (Proposed) + California CPRA GPC.
    """
    signals = {
        "gpc": False,        # Global Privacy Control
        "dnt": False,        # Do Not Track (deprecated but still sent)
        "sec_gpc": False,    # Sec-GPC header (IETF draft)
    }
    
    # Global Privacy Control — RFC: https://globalprivacycontrol.github.io/gpc-spec/
    # Sent as: Sec-GPC: 1
    sec_gpc = request_headers.get("sec-gpc", request_headers.get("Sec-GPC", ""))
    if sec_gpc == "1":
        signals["gpc"] = True
        signals["sec_gpc"] = True
    
    # Do Not Track — deprecated but widely sent
    # Sent as: DNT: 1
    dnt = request_headers.get("dnt", request_headers.get("DNT", ""))
    if dnt == "1":
        signals["dnt"] = True
    
    return signals


def should_block_analytics(consent_signals: dict, user_consent_record: Optional[dict]) -> bool:
    """
    Determine whether analytics should be blocked for this request.
    
    Under EU ePrivacy Art. 5(3): block unless prior consent given.
    Under proposed Art. 10: browser signal is a valid consent withdrawal.
    """
    # If user has given explicit consent via CMP, allow regardless of browser signal
    if user_consent_record and user_consent_record.get("analytics") == "granted":
        # But GPC overrides CMP consent under CPRA (US) — for EU, interpretation pending
        # Conservative approach: honor GPC even over prior CMP consent
        if consent_signals.get("gpc"):
            return True  # Block: GPC withdrawal overrides stored consent
        return False  # Allow: valid consent exists
    
    # No consent record: block by default
    return True

3. Machine-to-Machine Communications

The Proposal exempts machine-to-machine (M2M) communications that do not involve individual humans — IoT sensor data, industrial telemetry. This is cleaner than the Directive, which creates ambiguity for automated systems.

4. Consent Under ePR vs Current Directive

The Proposal largely aligns consent requirements with GDPR standards (GDPR-quality consent). The main addition: granular consent per purpose — users must be able to consent to analytics separately from advertising separately from personalization.

Why the Reform Has Stalled

The ePR Proposal has been blocked in the Council for years due to:

  1. Industry lobbying (advertising industry, telcos) against extended OTT scope and browser-level consent
  2. Member state disagreements on enforcement competence (should the lead DPA model from GDPR apply?)
  3. Interaction with DSA — the Digital Services Act (effective February 2024) creates overlapping obligations for large platforms around advertising transparency

The Council's 2021 and 2023 General Approaches narrowed scope significantly. The European Parliament's position was more expansive. As of 2026, no final text has been agreed.

Practical implication: Continue complying with Directive 2002/58/EC. Design consent architecture that is extensible for when (if) the Regulation is eventually adopted.

TCF 2.0: Industry Framework

The IAB Europe Transparency and Consent Framework (TCF) 2.0 is the advertising industry's standard mechanism for communicating user consent signals between publishers, consent management platforms (CMPs), and ad tech vendors.

How TCF 2.0 Works

User visits website
      ↓
CMP (Consent Management Platform) shows cookie banner
      ↓
User makes choices (accept all / reject all / granular)
      ↓
CMP generates TC String (Transparency & Consent String)
      ↓
TC String stored in:
  - Cookie: euconsent-v2
  - LocalStorage: IABTCF_TCString
      ↓
TC String passed to:
  - Google Ads (via GPT)
  - DV360, AppNexus, The Trade Desk
  - All vendors in TCF Global Vendor List
      ↓
Vendor reads TC String → determines whether consent given for their purpose

The TC String encodes:

The Belgian DPA (APD) declared TCF 2.0 non-compliant with GDPR in February 2022 (APD Decision 21/2022). Key findings:

  1. No freely given consent — "accept all" banners presented as the easy option, reject requires multiple clicks
  2. IAB Europe as joint controller — APD found IAB Europe was a joint controller for consent signals in the TC String, not merely a framework provider
  3. TC String = personal data — the string uniquely identifies a user's consent history, therefore is personal data under GDPR
  4. Legitimate interests misused — vendors were claiming legitimate interests for purposes requiring consent

IAB Europe submitted a corrective action plan, partially accepted by APD. TCF 2.1 addresses some issues but the fundamental tension between ad-tech economics and ePrivacy consent requirements remains.

For developers: Using a TCF 2.0 CMP does not guarantee compliance. You remain responsible for:

# Consent record structure — what you need to store for accountability
import hashlib
import json
from datetime import datetime, timezone
from typing import Literal

ConsentPurpose = Literal["analytics", "advertising", "personalization", "session_recording"]
ConsentValue = Literal["granted", "denied", "not_asked"]

class ConsentRecord:
    """
    Immutable consent record for ePrivacy + GDPR accountability.
    Store in database with user_id or session_id as key.
    
    Required under GDPR Art. 7(1): controller must demonstrate consent was given.
    Required under ePrivacy: prior consent before cookie/tracker activation.
    """
    def __init__(
        self,
        user_identifier: str,  # Pseudonymous — hashed session/user ID
        purposes: dict[ConsentPurpose, ConsentValue],
        cmp_version: str,
        banner_id: str,  # Which banner was shown (A/B test variant, version)
        interaction: Literal["accept_all", "reject_all", "custom", "browser_signal"],
        ip_truncated: str,  # Last octet removed: 192.168.1.0/24 → "192.168.1.x"
        user_agent_hash: str,  # Hashed, not stored in plaintext
        jurisdiction: str = "EU",
    ):
        self.timestamp_utc = datetime.now(timezone.utc).isoformat()
        self.user_identifier = hashlib.sha256(user_identifier.encode()).hexdigest()
        self.purposes = purposes
        self.cmp_version = cmp_version
        self.banner_id = banner_id
        self.interaction = interaction
        self.ip_truncated = ip_truncated
        self.user_agent_hash = hashlib.sha256(user_agent_hash.encode()).hexdigest()
        self.jurisdiction = jurisdiction
    
    def to_audit_record(self) -> dict:
        return {
            "timestamp": self.timestamp_utc,
            "user_id_hash": self.user_identifier,
            "purposes": self.purposes,
            "cmp_version": self.cmp_version,
            "banner_id": self.banner_id,
            "interaction_type": self.interaction,
            "ip_truncated": self.ip_truncated,
            "ua_hash": self.user_agent_hash,
            "jurisdiction": self.jurisdiction,
        }
    
    def consent_given_for(self, purpose: ConsentPurpose) -> bool:
        return self.purposes.get(purpose) == "granted"

Tracking Pixels and Server-Side Tracking

Tracking Pixels Under ePrivacy

A tracking pixel is a 1×1 transparent image loaded from a third-party server. When the browser requests the pixel, the third-party server logs:

Tracking pixels do not set cookies in the browser but they do access information stored on the device — specifically, they read browser cache, user agent, and cookies already stored for that domain. Under Art. 5(3), this access to stored device information requires consent.

Email tracking pixels are particularly problematic: they operate in a context where the user cannot see a cookie banner (email inbox). CNIL, ICO, and DE DSK guidance consistently treats email tracking pixels as requiring prior consent under both the ePD and GDPR.

Server-Side Tracking

Server-side tracking moves tag execution from the browser to a server you control. Instead of loading Google Tag Manager directly in the browser (which allows Google's cookies to be set), you run a server-side GTM container that:

  1. Receives events from the browser (no third-party cookies)
  2. Processes and enriches data server-side
  3. Forwards to analytics/ads vendors with your own first-party identifiers

Server-side tracking reduces third-party cookie scope but does not eliminate ePrivacy consent requirements:

# Server-side tracking consent gate
# Use this middleware to check consent before forwarding events

async def tracking_event_handler(request, event_payload: dict, user_session: dict) -> None:
    """
    Server-side tracking middleware with ePrivacy consent gate.
    Must be called before forwarding ANY event to third-party analytics/ads vendors.
    """
    consent = await get_consent_record(user_session.get("consent_id"))
    
    routing = {
        "analytics_vendors": ["google_analytics", "segment", "amplitude"],
        "ads_vendors": ["google_ads", "meta_capi", "linkedin_insight"],
        "session_recording": ["hotjar_api", "fullstory_api"],
    }
    
    allowed_destinations = []
    
    if consent and consent.consent_given_for("analytics"):
        allowed_destinations.extend(routing["analytics_vendors"])
    
    if consent and consent.consent_given_for("advertising"):
        allowed_destinations.extend(routing["ads_vendors"])
    
    if consent and consent.consent_given_for("session_recording"):
        allowed_destinations.extend(routing["session_recording"])
    
    for destination in allowed_destinations:
        await forward_event(destination, event_payload)
    
    # Log routing decision for audit trail
    await log_routing_decision(
        event_id=event_payload.get("event_id"),
        allowed=allowed_destinations,
        blocked=[d for vendors in routing.values() for d in vendors if d not in allowed_destinations],
        consent_record_id=consent.user_identifier if consent else None,
    )

CLOUD Act × ePrivacy: The Jurisdiction Problem

US cloud providers are subject to the CLOUD Act (18 U.S.C. § 2713), which requires US-based companies to produce data in response to US court orders regardless of where the data is physically stored. This creates a direct conflict with EU ePrivacy obligations:

The conflict:

EU ePrivacy ObligationCLOUD Act Risk
Consent records must be stored securelyUS provider can be compelled to disclose consent logs
Communication content is confidential (Art. 5(1))US DOJ can request email/chat content stored on US server
Traffic metadata must be minimized (Art. 6)US warrant can reach CDN access logs
Consent management infrastructure (cookies, TC strings)If hosted on US CMP → US legal access possible

Practical developer exposure:

If your CMP (Consent Management Platform) is hosted on a US cloud provider:

If your analytics data (post-consent) is processed by US companies (Google, Meta, LinkedIn):

EU-native PaaS approach: Running your consent management infrastructure and analytics on EU-native infrastructure (servers physically in the EU, operated by EU-law-governed entities) removes the CLOUD Act reach. The company storing your consent logs is not a US person or US company, so 18 U.S.C. § 2713 does not apply.

US Cloud Provider CMP:
  Consent record → US company server → CLOUD Act reachable

EU-Native PaaS CMP:
  Consent record → EU-company server → CLOUD Act NOT applicable
  Single jurisdiction: EU ePrivacy + GDPR only

Developer Compliance Checklist

Before Launch (Mandatory)

Not all analytics require consent. Truly cookieless, privacy-respecting analytics that aggregate data without cross-session user tracking fall outside Art. 5(3) scope:

ToolConsent Required?Notes
Plausible AnalyticsNo (with configuration)No cookies, IP-based aggregation, no cross-site tracking
Fathom AnalyticsNoPrivacy-first, cookieless mode
UmamiNo (self-hosted)Open source, no cookies in default config
MatomoDependsCookie mode = consent required; cookieless mode = no consent
Google Analytics 4YesSets cookies, fingerprints users, US data transfer
Simple AnalyticsNoNo cookies, no personal data

Documentation

Enforcement Landscape: What DPAs Are Actually Fining

CNIL (France) — Most Active Enforcer

YearCompanyFineReason
2022Google LLC€150MCookie rejection mechanism harder than acceptance
2022Facebook€60MNo cookie refusal button on first page
2022Google Ireland€90MCookie rejection mechanism harder than acceptance
2023TikTok€5MRejection of cookies not as easy as acceptance
2024Apple€8MApp Tracking Transparency — pre-ticked boxes

CNIL's enforcement focus: banner design — particularly whether the "reject all" option is as prominent and accessible as "accept all."

German DSK / LDA Bayern

German DPAs have focused on:

Belgian APD — TCF 2.0 Systemic Case

The Belgian APD's TCF 2.0 ruling (2022) is the most significant systemic enforcement action. It established that:

ICO (UK) — Adtech Investigation

The ICO's Real-Time Bidding (RTB) investigation (2019-2024) found systemic ePrivacy violations across the programmatic advertising ecosystem. While the ICO has not issued the large fines the CNIL has, it has issued enforcement notices and the RTB investigation remains open.

Practical Architecture: What to Build

// Next.js: block all third-party scripts before consent
// Use next/script with strategy="afterInteractive" gated on consent state

"use client";
import Script from "next/script";
import { useConsentStore } from "@/stores/consent";

export function ConditionalAnalytics() {
  const { analytics, advertising } = useConsentStore();
  
  return (
    <>
      {/* Only load after consent granted — afterInteractive fires post-hydration */}
      {analytics && (
        <Script
          src="https://plausible.io/js/script.js"
          data-domain="yourdomain.com"
          strategy="afterInteractive"
        />
      )}
      
      {/* Example: server-side Facebook CAPI — no browser pixel, but still needs consent */}
      {/* Server-side event sent from your server, not from browser */}
      {/* Consent check happens server-side before event is forwarded */}
    </>
  );
}
// Consent store — Zustand example
// Persists consent across sessions, respects GPC signal

import { create } from "zustand";
import { persist } from "zustand/middleware";

interface ConsentState {
  analytics: boolean;
  advertising: boolean;
  personalization: boolean;
  sessionRecording: boolean;
  consentTimestamp: string | null;
  bannerVersion: string | null;
  setConsent: (purposes: Partial<Omit<ConsentState, "setConsent" | "consentTimestamp" | "bannerVersion">>) => void;
  revokeAll: () => void;
}

export const useConsentStore = create<ConsentState>()(
  persist(
    (set) => ({
      analytics: false,
      advertising: false,
      personalization: false,
      sessionRecording: false,
      consentTimestamp: null,
      bannerVersion: null,
      setConsent: (purposes) =>
        set((state) => ({
          ...state,
          ...purposes,
          consentTimestamp: new Date().toISOString(),
          bannerVersion: "2.1.0",  // bump when banner text or purposes change
        })),
      revokeAll: () =>
        set({
          analytics: false,
          advertising: false,
          personalization: false,
          sessionRecording: false,
          consentTimestamp: new Date().toISOString(),
          bannerVersion: "2.1.0",
        }),
    }),
    { name: "consent-store" }
  )
);

EU-Native Hosting and ePrivacy

Choosing EU-native infrastructure for consent management and analytics processing:

  1. Single regulatory regime: EU ePrivacy + GDPR — no CLOUD Act overlap
  2. No Chapter V GDPR transfer risk: data stays in EU jurisdiction — no SCCs required for consent logs
  3. DPA audit readiness: consent records stored on EU soil accessible to EU DPAs under EU legal process — not accessible via CLOUD Act without EU judicial cooperation
  4. TCF 2.0 consent logs: if you store TC strings on EU-native servers, they fall under EU law exclusively

When a DPA requests your consent logs as part of a complaint investigation, the legal process runs entirely through EU channels. No parallel US discovery risk.

See Also