GDPR Art. 13(1)(c) requires disclosure of 'the purposes of the processing for which the personal data are intended as well as the legal basis for the processing.' For cookie-based tracking, this means naming the specific cookies, their retention periods, and who processes the data — not just stating 'we use analytics cookies.' CNIL enforcement guidelines specifically require per-cookie documentation by name. When a cookie policy lists only category descriptions, users cannot identify individual trackers, compare them against browser developer tools, or make meaningful decisions about specific processing activities.
Info because the failure is a documentation gap that reduces consent quality and regulatory defensibility rather than causing active unlawful processing — the consent mechanism and enforcement can still function without it.
Generate the per-cookie table directly from COOKIE_REGISTRY so the policy stays current automatically without manual maintenance.
// app/cookies/page.tsx
import { COOKIE_REGISTRY } from '@/lib/cookies/registry'
export default function CookiePolicyPage() {
const categories = ['essential', 'analytics', 'marketing', 'preferences'] as const
return (
<main className="max-w-3xl mx-auto px-4 py-12">
<h1 className="text-2xl font-bold mb-2">Cookie Policy</h1>
{categories.map(cat => {
const cookies = COOKIE_REGISTRY.filter(c => c.category === cat)
if (!cookies.length) return null
return (
<section key={cat} className="mt-8">
<h2 className="text-lg font-semibold capitalize mb-3">{cat} Cookies</h2>
<table className="w-full text-sm border-collapse">
<thead>
<tr className="border-b">
<th className="text-left py-2">Name</th>
<th className="text-left py-2">Purpose</th>
<th className="text-left py-2">Duration</th>
<th className="text-left py-2">Provider</th>
</tr>
</thead>
<tbody>
{cookies.map(c => (
<tr key={c.name} className="border-b">
<td className="py-2"><code>{c.name}</code></td>
<td className="py-2">{c.purpose}</td>
<td className="py-2">{c.expiry}</td>
<td className="py-2">{c.provider}</td>
</tr>
))}
</tbody>
</table>
</section>
)
})}
</main>
)
}
ID: cookie-consent-compliance.cookie-policy.per-cookie-documentation
Severity: info
What to look for: Read the cookie policy page content (the source file, not the rendered page). Evaluate whether it lists cookies individually — not just "we use analytics cookies" but the specific cookie names. For each cookie, check whether these four fields are present: (1) exact cookie name (e.g., _ga) or name pattern (e.g., _ga_*), (2) purpose in plain language, (3) expiry/duration, (4) provider (first party vs. named third party). ICO guidance recommends per-cookie documentation. CNIL (France) requires per-cookie documentation for compliant consent. Compare the cookie policy page against the cookie registry — do they match?
Pass criteria: Count all unique cookies and check documentation for each. Cookie policy lists each individual cookie (or cookie pattern for dynamically named cookies like _ga_XXXXXXXX) with name, purpose, duration, and provider. The list is comprehensive and matches the actual cookies set by the application. At least 90% of cookies must have documented name, purpose, duration, and category.
Fail criteria: Cookie policy only describes cookies at a category level ("we use analytics cookies") without naming individual cookies. Policy lists some cookies but not all. Policy lists cookie names but omits purpose, duration, or provider for most entries.
Skip (N/A) when: No cookie policy page exists (already failing at cookie-policy-page).
Detail on fail: Example: "Cookie policy describes cookie categories (Analytics, Marketing) but does not list individual cookie names, durations, or providers." or "Cookie policy lists _ga but does not include _ga_XXXXXXXX (the GA4 measurement ID cookie), and no expiry or provider listed.".
Remediation: Generate the per-cookie table programmatically from the registry to keep it always current:
// app/cookies/page.tsx
import { COOKIE_REGISTRY } from '@/lib/cookies/registry'
export default function CookiePolicyPage() {
return (
<main>
<h1>Cookie Policy</h1>
<p>Last updated: {new Date().toLocaleDateString()}</p>
<p>This page explains exactly what cookies we use, why we use them, and how long we keep them.</p>
{(['essential', 'analytics', 'marketing', 'preferences'] as const).map(category => {
const cookies = COOKIE_REGISTRY.filter(c => c.category === category)
if (cookies.length === 0) return null
return (
<section key={category}>
<h2>{category.charAt(0).toUpperCase() + category.slice(1)} Cookies</h2>
<table>
<thead>
<tr>
<th>Cookie Name</th>
<th>Purpose</th>
<th>Duration</th>
<th>Provider</th>
</tr>
</thead>
<tbody>
{cookies.map(cookie => (
<tr key={cookie.name}>
<td><code>{cookie.name}</code></td>
<td>{cookie.purpose}</td>
<td>{cookie.expiry}</td>
<td>{cookie.provider}</td>
</tr>
))}
</tbody>
</table>
</section>
)
})}
</main>
)
}