Skip to content
Back to Blog
Editorial-Stillleben: ein in zwei Haelften geteiltes Plaetzchen auf cremefarbenem Papier, symbolisiert die binaere Cookie-Einwilligung
|Updated: |4 min read|3

Cookie consent done right: what § 25 TDDDG actually requires

AI-generatedclaude-opus-4-7compliancecookie-consenttdddgttdsgdsgvonext-jsrecht

Cookie banners are among the most frequently misimplemented UI elements on the German web. Almost every second site I audit violates the TDDDG (Telecommunications-Digital-Services Data Protection Act) — usually without the operator knowing. This article explains what the law requires, the most common mistakes I see, and how a GDPR-compliant banner is built technically.

The basics: § 25 TDDDG

Since 14 May 2024 the law previously known as TTDSG is called TDDDG. The key paragraph stayed the same: § 25 TDDDG requires active consent from the user before information is stored in their end device — whether classic cookies, Local Storage, Session Storage or fingerprinting techniques. The only exception is strictly technically necessary storage like session cookies for login or a CSRF token.

The legal basis for the TDDDG is the EU ePrivacy Directive, complemented by the GDPR. On top there are two important rulings that are often overlooked in practice:

  • CJEU "Planet49" (C-673/17, 2019): pre-checked boxes are not valid consent. The user has to click actively.
  • German DSK ruling of 22 August 2024: rejecting must be possible with the same effort and at least the same prominence as accepting. If "Accept" is designed as an eye-catching primary button and "Reject" is hidden in a grey secondary, the consent is not freely given — and therefore invalid.

The five most common violations

1. UX inequality between Accept and Reject

This is the clearest and most documented violation. Classic case: "Accept all" glows in brand colour, "Reject" is a discreet grey text link. Fix: both buttons equally weighted, same background, same hover. No dark patterns.

// Bad — dark pattern
<button className="bg-orange-500 ...">Accept all</button>
<button className="bg-slate-700 ...">Necessary only</button>

// Good — UX-equal, DSK-compliant
<button className="bg-orange-500 ...">Necessary only</button>
<button className="bg-orange-500 ...">Accept all</button>

2. Banner loads trackers before the user decides

If the CMP asks for consent but Google Analytics, Facebook Pixel or embedded YouTube frames are already active on first page load, that is a serious violation. I check this in every audit with Playwright: open the browser fresh, load the page, evaluate cookies and third-party requests before any interaction. Tracker URLs in there? → violation.

3. No granularity

You need at least three categories: Necessary, Statistics, Marketing. Anyone bundling everything into a single "Accept" button doesn't have valid consent — the user can't decide what to allow.

4. No withdrawal

Consent must be revocable at any time with the same effort it was granted. A cookie banner that never appears again after acceptance and offers no "change cookie settings" link in footer or privacy policy is not compliant.

5. No re-consent on changes

When new third parties are added or existing ones change their purposes, consent has to be requested again. Consent given two years ago for "marketing" doesn't cover a freshly added TikTok pixel.

How a compliant banner looks technically

A clean cookie banner in Next.js with a Server-Components-compatible pattern sets nothing before consent. Only after an active click are the selected scripts loaded:

"use client";

const COOKIE_CONSENT_KEY = "consent";
const COOKIE_PREFERENCES_KEY = "consent_prefs";

export function CookieBanner() {
  const [visible, setVisible] = useState(false);
  const [prefs, setPrefs] = useState({
    necessary: true,    // always active
    statistics: false,
    marketing: false,
  });

  useEffect(() => {
    if (!localStorage.getItem(COOKIE_CONSENT_KEY)) {
      setVisible(true);
    }
  }, []);

  const save = (p: typeof prefs) => {
    localStorage.setItem(COOKIE_CONSENT_KEY, new Date().toISOString());
    localStorage.setItem(COOKIE_PREFERENCES_KEY, JSON.stringify(p));
    setVisible(false);
    window.dispatchEvent(new CustomEvent("consentChanged", { detail: p }));
  };

  // Other components listen for "consentChanged" and load their scripts
  // only afterwards.
}

Important: statistics/marketing scripts are not embedded directly in the layout but loaded via a listener reacting to the `consentChanged` event. Before the event nothing is active — verified by Playwright audit.

Audit trail: what to store, what not

Supervisory authorities can ask for proof that user X consented on day Y to a specific configuration. A pure localStorage entry is not enough — that's not provable. I log consents server-side in a dedicated table:

  • Hash of the preferences (e.g. SHA-256 over the settings JSON)
  • Timestamp
  • User agent (truncated, no IP — that's personal data)
  • Optional session ID or anonymous visitor ID

Retention: 3 years after last consent — then delete. The period derives from civil-law statute of limitations on damage claims.

What I do concretely

In a compliance audit I systematically check all the above with Playwright and a manual walk-through. Typical procedure: first reject all preferences, then observe what cookies, LocalStorage entries and third-party requests still appear. Then the same with "Accept all". The delta is where the banner delivers what it promises — or doesn't.

On the implementation side I take care of selecting and integrating a GDPR-compliant CMP (self-hosted or EU provider, no US platform), a Server-Components-compatible pattern in Next.js, consent logging in Supabase EU, testing the reject path with a real browser, updating the privacy policy with current providers, and a re-consent workflow when the cookie list changes.

More details and an audit offer for your project at /compliance/cookie-consent.

AI-Generated Article

This article was created with AI assistance based on external sources and reviewed by Harald Schwankl. All phrasing is original.

AI Model: claude-opus-4-7 · Confidence: 85%

Share with friends & colleagues
Topics:
COMPLIANCECOOKIE-CONSENTTDDDGTTDSGDSGVONEXT-JSRECHT
Be the first

How helpful did you find this page?

Harald Schwankl

Dipl.-Ing. Electrical Engineering · Fullstack Developer · AI Specialist

Fullstack developer with over 20 years of experience in software engineering. Specializing in AI integration, RAG systems and AI agents. I build industry solutions that are performant, scalable and smart.

Related Articles

New articles by email

Once a week in digest form: new articles on AI, fullstack and web. Double-opt-in, unsubscribe anytime.

Newsletter

Monthly tips on AI, web development, and RAG.

We respect your privacy. Unsubscribe at any time.

Made in Germany100% DSGVO-konformEU AI Act ReadySicheres HostingBarrierefreiCookie ConsentDaten-Anonymisierung
Cookie consent done right: what § 25 TDDDG actually requires