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:
- Public electronic communications networks
- Publicly available electronic communications services
- By extension: websites and web applications that set or read information on user devices
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:
- LocalStorage and SessionStorage
- IndexedDB writes
- Cache API writes
- JavaScript fingerprinting (as "access to information already stored")
- Flash cookies (now legacy, but established the principle)
- ETags used for tracking
- CNAME-cloaked analytics (debated, but Belgian DPA ruled these in-scope)
The exceptions to the consent requirement (Art. 5(3) second sentence) are narrow:
- Technical necessity for the transmission of communication (session routing)
- 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:
- Email content stored on mail servers
- Metadata of electronic communications (sender, recipient, time, location)
- Chat message content in OTT services
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:
- CDN providers logging IP geolocation per request
- Mobile app developers processing GPS coordinates
- Web analytics platforms combining location with session data
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:
| Dimension | EU (2002/58/EC) | UK PECR |
|---|---|---|
| Enforcer | National DPAs (27 member states) | ICO |
| Cookie consent | Prior consent, strict exceptions | Prior consent, same narrow exceptions |
| Soft opt-in (marketing) | Not harmonized across member states | Regulation 22(3) — allowed for existing customers |
| Max fine | Varies by member state (GDPR level in some) | £500,000 (PECR-specific cap) |
| Jurisdiction | EU users or EU establishment | UK 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)):
- (a) Consent
- (b) Contract
- (c) Legal obligation
- (d) Vital interests
- (e) Public task
- (f) Legitimate interests
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:
- ePrivacy prior consent (before any cookie is set)
- GDPR Art. 6(1)(a) consent as the lawful basis for data processing (must match ePD consent — one consent mechanism covers both)
Consent Requirements Under ePrivacy + GDPR
For consent to be valid under both frameworks simultaneously (EDPB Guidelines 05/2020), it must be:
| Requirement | GDPR Art. 7 | ePrivacy Art. 5(3) |
|---|---|---|
| Freely given | Yes — no bundling with service | Yes — equivalent standard |
| Specific | Per processing purpose | Per cookie category |
| Informed | Privacy notice required | Cookie disclosure required |
| Unambiguous indication | Active opt-in | Active opt-in (no pre-ticked boxes) |
| Withdrawable | As easy as giving | As easy as giving |
| Prior to processing | Required | Required ("before... storage or access") |
| Documented | Controller burden | Controller 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.
Analytics Cookies: The Legal Exposure Map
# 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:
- Browser software to offer users the option to prevent third parties from storing information on their device
- Default settings must not allow third-party tracking (if technically possible)
- Browser consent signals would replace or supplement per-site cookie banners
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:
- Industry lobbying (advertising industry, telcos) against extended OTT scope and browser-level consent
- Member state disagreements on enforcement competence (should the lead DPA model from GDPR apply?)
- 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:
- Version, timestamp
- CMP ID and CMP version
- Consent for each of IAB's 10 "purposes" (store/access info, personalization, measurement, etc.)
- Vendor-specific consent (Global Vendor List ID)
- Legitimate interest signals (per vendor)
TCF 2.0 Legal Problems
The Belgian DPA (APD) declared TCF 2.0 non-compliant with GDPR in February 2022 (APD Decision 21/2022). Key findings:
- No freely given consent — "accept all" banners presented as the easy option, reject requires multiple clicks
- 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
- TC String = personal data — the string uniquely identifies a user's consent history, therefore is personal data under GDPR
- 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:
- Banner design (must not be "dark patterns")
- Default settings (non-consent-related tracking must be off by default)
- Reject mechanism (as easy to access as accept)
- Documentation of consent (GDPR Art. 7(1) accountability)
# 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:
- IP address
- User agent
- Referrer URL
- Timestamp
- Any query parameters encoded in the pixel URL
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:
- Receives events from the browser (no third-party cookies)
- Processes and enriches data server-side
- 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:
- If a first-party cookie is set to create a persistent identifier across sessions → requires consent
- If server-side events are linked to user identifiers derived from prior cookie-based tracking → consent for the original cookie propagates
- If data is sent to Google/Meta/LinkedIn → GDPR international transfer analysis required (CLOUD Act risk)
# 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 Obligation | CLOUD Act Risk |
|---|---|
| Consent records must be stored securely | US 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:
- Your consent records (proof of compliance) can be accessed by US authorities
- This does not directly expose end users to US surveillance, but it exposes your compliance infrastructure
- If the consent record is linked to a user identifier, that identifier + consent history can be accessed
If your analytics data (post-consent) is processed by US companies (Google, Meta, LinkedIn):
- Data transfer requires a valid transfer mechanism (SCCs, BCRs) under GDPR Art. 46
- The Schrems II ruling (C-311/18) established that SCCs may be insufficient without additional safeguards if the processor is subject to US surveillance law
- The EU-US Data Privacy Framework (DPF, July 2023) provides a mechanism but is challenged by Max Schrems (Schrems III, pending)
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)
- Inventory all technologies that store or read device information: cookies, LocalStorage, IndexedDB, fingerprinting scripts, tracking pixels, third-party scripts loaded in
<head>or via tag manager - Classify each by consent category: strictly necessary (no consent) vs. functional, analytics, advertising, session recording (all require prior consent)
- Block by default: ensure no non-necessary cookies are set or scripts loaded before consent is obtained
- Implement prior consent gate: consent banner fires before any non-necessary tracking code executes — test with JS disabled, then re-enabled after consent
- Design banner for GDPR-quality consent: clear accept/reject options at same prominence level, no pre-ticked boxes, no dark patterns
Consent Infrastructure
- Store consent records with: timestamp (UTC), user identifier (pseudonymous), purposes (per category), banner version, interaction type — for GDPR Art. 7(1) accountability
- Withdrawal mechanism: cookie preferences link in footer, same number of clicks to withdraw as to consent
- Consent expiry: re-ask consent after a defined period (CNIL recommends max 13 months; German DSK recommends 6 months for advertising consent)
- Honor browser signals: implement Sec-GPC header detection; if GPC=1, treat as consent withdrawal for tracking (required under CPRA for California; consider for EU users pending Art. 10 ePR)
Analytics Without Consent (Privacy-Respecting Alternatives)
Not all analytics require consent. Truly cookieless, privacy-respecting analytics that aggregate data without cross-session user tracking fall outside Art. 5(3) scope:
| Tool | Consent Required? | Notes |
|---|---|---|
| Plausible Analytics | No (with configuration) | No cookies, IP-based aggregation, no cross-site tracking |
| Fathom Analytics | No | Privacy-first, cookieless mode |
| Umami | No (self-hosted) | Open source, no cookies in default config |
| Matomo | Depends | Cookie mode = consent required; cookieless mode = no consent |
| Google Analytics 4 | Yes | Sets cookies, fingerprints users, US data transfer |
| Simple Analytics | No | No cookies, no personal data |
Documentation
- Privacy notice (GDPR Art. 13/14): discloses each cookie/tracker, its purpose, retention, and recipients — updated when new cookies added
- Cookie policy (separate page): lists all cookies by name, category, duration, and whether first or third party
- Processor agreements (GDPR Art. 28): DPA with each analytics/ads vendor that receives personal data
- Data transfer mechanisms (GDPR Art. 46): SCCs or DPF certification for US vendors
Enforcement Landscape: What DPAs Are Actually Fining
CNIL (France) — Most Active Enforcer
| Year | Company | Fine | Reason |
|---|---|---|---|
| 2022 | Google LLC | €150M | Cookie rejection mechanism harder than acceptance |
| 2022 | €60M | No cookie refusal button on first page | |
| 2022 | Google Ireland | €90M | Cookie rejection mechanism harder than acceptance |
| 2023 | TikTok | €5M | Rejection of cookies not as easy as acceptance |
| 2024 | Apple | €8M | App 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:
- Google Analytics without valid consent mechanism (2022 guidelines)
- US data transfers to Google (Schrems II compliance gap)
- CNAME cloaking attempts by publishers to circumvent third-party cookie rules
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:
- IAB Europe's TC String is personal data
- The industry's self-regulatory framework is insufficient
- Individual website operators remain liable even when using "compliant" CMPs
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
Recommended Stack for EU Compliance
// 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:
- Single regulatory regime: EU ePrivacy + GDPR — no CLOUD Act overlap
- No Chapter V GDPR transfer risk: data stays in EU jurisdiction — no SCCs required for consent logs
- 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
- 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
- EU GDPR Art. 25 Privacy-by-Design Guide for PaaS Developers — privacy by design principles that complement ePrivacy
- EU Digital Services Act 2024 Hosting Provider Guide — DSA Art. 14-17 obligations overlap with ePrivacy for large platforms
- EU NIS2 + AI Act Critical Infrastructure Compliance — NIS2 confidentiality obligations align with ePD Art. 5(1)
- EU AI Liability Directive Developer Guide — AI Act + AILD liability exposure for consent management AI
- EU Digital Markets Act Developer Guide — DMA Art. 5(2) prohibits conditioning access on consent to tracking across services