Cookie banners: when a small site needs one, and how to check
EU law asks for consent before anything not strictly needed is stored or read on a visitor's device. The UK loosened that for analytics in 2026.
The short read
EU cookie rules cover any storage or reading on a visitor's device, tracking pixels and scripts included, not only cookies.
- A site storing only what the requested page strictly needs requires no banner. In the EU, analytics usually does not count as strictly needed.
- Since February 5, 2026, UK law exempts first-party statistics and display preferences if visitors are told and can object for free.
What you can do with this
Open your site in a fresh browser profile and list every cookie, storage key and outside request made before any click. In the EU, anything not strictly necessary generally needs consent first.
In this article
- The rule covers storage and access, not only cookies
- Strictly necessary is judged from the visitor’s side
- Analytics is where the EU and the UK now differ
- The UK made statistics an exception in 2026
- When a banner is needed, the consent standard is the GDPR’s
- Our test: what two sample sites set before any click
- The script, and where it falls short
- What this means for you
- How we made this
A small site needs a cookie banner when it stores or reads anything on a visitor’s device that is not strictly necessary for the page the visitor asked for. That is the test in Article 5(3) of the EU ePrivacy Directive, and it reaches past cookies. Analytics, ad pixels and many embeds fail it, so they need consent first. A site with nothing else on it can skip the banner.
The UK changed its version of the rule on February 5, 2026. First-party statistics and display preferences no longer need consent there, as long as visitors are told and can object.
On October 24, 2026, we read the EU and UK texts, the regulators’ guidance and the leading court ruling. We also wrote a script that lists what a page stores and sends before any click, and ran it on two sample sites we built. This is a reading of the sources, not legal advice.
The rule covers storage and access, not only cookies
Article 5(3) allows “the storing of information, or the gaining of access to information already stored” on a user’s device only with consent. The user must first get “clear and comprehensive information” about the purposes.
The article names two exceptions. One covers storage needed to carry a communication over a network. The other covers what is “strictly necessary” for a service “explicitly requested by the subscriber or user.”
The word “cookie” does not appear in that sentence. The European Data Protection Board (EDPB) adopted the final version of its Guidelines 2/2023 on October 7, 2024. They say the article “does not exclusively apply to cookies, but also to ‘similar technologies’.”
The guidelines bring in tracking pixels, tracking links and JavaScript that tells the browser to send information back. They also cover locally stored data such as localStorage once it leaves the device. IP-based tracking can fall under the rule too, unless the entity can show the address does not come from the user’s own equipment.
Personal data is not the trigger either. The EU Court of Justice ruled on this in Planet49 (case C-673/17, October 1, 2019). The consent rule applies, it held, “whether or not the information stored or accessed on the user’s equipment is personal data.” So a “cookieless” analytics script that reads from the browser and reports back is not automatically outside the rule.
Strictly necessary is judged from the visitor’s side
The EU’s data protection advisers at the time, the Article 29 Working Party, set out what can skip consent in Opinion 04/2012. Its list, each item “if they are not used for additional purposes”:
- user input cookies, such as a shopping cart session, for the session or a few hours
- authentication cookies, for the session
- security cookies that detect login abuse, for a limited time
- media player session cookies
- load balancing session cookies
- interface preference cookies, “for the duration of a session (or slightly more)”
- social plug-in sharing cookies, for members who are logged in
Three rules come with it. Necessity is judged “from the point of view of the user, not the service provider.” A cookie with several purposes is exempt only if every purpose is. And the purpose decides, not whether the cookie is first-party or session-only.
Analytics is not on the list. The opinion says first-party analytics cookies “are not exempt from consent but pose limited privacy risks” when they come with clear information, an easy opt-out and anonymization. Third-party advertising cookies “cannot be exempted,” including for frequency capping and fraud detection tied to ads.
Analytics is where the EU and the UK now differ
EU countries write the directive into their own laws, and regulators read the analytics question differently. France’s CNIL lets audience measurement skip consent under conditions on its analytics sheet. Visitors are told and can object. The purpose is limited to audience measurement and A/B testing, and the data is not combined with other processing.
The CNIL’s tracker must also stay on one site or app publisher, the last byte of the IP address is cut, and trackers live at most 13 months. The CNIL adds that most large audience measurement offerings fall outside the exemption whatever their settings. The EDPB’s cookie banner taskforce reminds site owners that national rules still apply on top of its common positions.
The UK made statistics an exception in 2026
The UK went further by statute. The Data (Use and Access) Act 2025 replaced regulation 6 of the UK’s privacy rules (PECR) and added Schedule A1 from February 5, 2026. Its paragraph 5 allows storage whose “sole purpose” is collecting “information for statistical purposes” about how a service or website is used, to improve it. The data may be shared only with someone who helps make those improvements.
Paragraph 6 covers adapting how a site “appears or functions” to the visitor’s preferences. Both require clear information and “a simple means of objecting, free of charge.”
The ICO’s guidance on the exceptions draws the limits. The statistics exception “does not apply to purposes related to online advertising.” Nor does it let you “monitor or track individual visitors.”
A third-party analytics provider is allowed, but it “must be a processor, not a joint controller.” If someone objects, you must stop. And “there are no advertising purposes that meet the strictly necessary exception.”
| What the site stores or sends | EU (Article 5(3) as read by WP29 and EDPB) | UK (PECR since February 5, 2026) |
|---|---|---|
| Login, cart, security, load balancing | No consent if strictly necessary for the requested service | No consent if strictly necessary |
| Remembering a choice the visitor made, such as language | Exempt for a session “or slightly more” (WP29) | Exempt with information and a free way to object |
| First-party statistics to improve the site | Consent, unless a national exemption applies (France: CNIL conditions) | Exempt with information and a free way to object; provider must be a processor |
| Ad pixels, retargeting, ad measurement | Consent | Consent |
| Third-party embeds that track (video, social) | Consent, unless a logged-in member asked for the feature | Consent when the plug-in tracks |
When a banner is needed, the consent standard is the GDPR’s
The directive borrows its definition of consent from data protection law. Under GDPR Article 4(11), consent is “freely given, specific, informed and unambiguous,” shown “by a statement or by a clear affirmative action.” Regulators have turned that into concrete banner rules:
- No pre-ticked boxes. Planet49 held that consent is not given by “a pre-checked checkbox which that user must deselect.” The EDPB taskforce confirmed this for second-layer settings.
- Scrolling is not consent. The EDPB’s consent guidelines (paragraph 86) say scrolling or swiping “will not under any circumstances” count.
- Reject next to Accept. In the 2023 taskforce, a “vast majority” of authorities held that a banner with no reject option on any layer breaks the rules. A reject link buried in a paragraph of text, without visual support, did not pass either.
- No cookie wall. The consent guidelines (paragraphs 39 to 41) say access must not depend on accepting cookies.
- Withdrawing as easy as giving. GDPR Article 7(3): “It shall be as easy to withdraw as to give consent.”
- Legitimate interest is no substitute. The taskforce agreed that the legal basis for placing or reading cookies “cannot be the legitimate interests of the controller.”
- Say how long and who else. Planet49 requires information on “the duration of the operation of cookies and whether or not third parties may have access.”
Our test: what two sample sites set before any click
Cookie scanners stop at listing. The EDPB taskforce noted that such tools “do not allow to check the nature of the cookies but only to list the cookies placed.” The site owner must still explain each purpose.
Our script does the same job: it opens a page in a fresh browser profile and records cookies, localStorage, sessionStorage and requests to other hosts, before any click. With --reject, it repeats the visit, clicks a Reject button and loads the page again.
We built two sample product pages with the same banner. The “broken” one runs its analytics on page load and ignores the Reject button. The “fixed” one loads analytics only after Accept. Both set a language cookie from the server, and the broken one also starts a server session for every visitor.
The analytics on both is our own code and mimics a common pattern: a 730-day cookie we named _ga_sample, a visitor ID in localStorage and an image pixel sent to a second host.
| Found before any click | Broken site | Fixed site |
|---|---|---|
| Cookies | lang (365 days), sess (session), _ga_sample (400 days) |
lang (365 days) |
| localStorage keys | an_visitor |
none |
| Requests to other hosts | 1 pixel carrying the visitor ID | none |
| After Reject and a second page view | Same three cookies plus consent, and the pixel fired again |
lang plus consent |
Three findings carry over to real sites. The tracking showed up in three places, and only one of them was a cookie, which is the EDPB’s point about similar technologies. Our script asked for a 730-day cookie and Chromium stored it for 400 days, because Chrome caps cookie lifetimes at 400 days since version 104. And a Reject button proves nothing until you check what happens after the click.
The fixed site is not spotless either. Its language cookie lasts a year, longer than the “session (or slightly more)” the WP29 opinion gives for exempt interface preferences. Under the UK’s new paragraph 6, the same cookie needs clear information and a free way to object. The consent cookie that stores the visitor’s choice is the kind the taskforce says should be deemed essential.
The script, and where it falls short
Save this as cookie-check.mjs, install Playwright (npm install playwright, then npx playwright install chromium) and run node cookie-check.mjs https://your-site.example/ --reject. We ran it with Node 22 and Playwright 1.63.
// cookie-check.mjs: list what a page stores and sends before any consent click,
// then (with --reject) what it stores after clicking a Reject button.
// Usage: node cookie-check.mjs <url> [--reject] [--wait=3000]
import { chromium } from 'playwright';
const url = process.argv[2];
const doReject = process.argv.includes('--reject');
const waitArg = Number((process.argv.find((a) => a.startsWith('--wait=')) || '').split('=')[1]);
const waitMs = Number.isFinite(waitArg) && waitArg >= 0 ? waitArg : 3000;
if (!url) { console.error('usage: node cookie-check.mjs <url> [--reject] [--wait=ms]'); process.exit(1); }
// Names that commonly belong to analytics or ad tools. A match is a reason to ask, not a verdict.
const KNOWN = [/^_ga/, /^_gid$/, /^_gcl_/, /^_fbp$/, /^_fbc$/, /^_hj/, /^_clck$/, /^_clsk$/, /^_uet/, /^ajs_/, /^mp_/, /^_pk_/];
const pageHost = new URL(url).hostname;
// Rough first-party test: same host or a parent domain of it. Good enough to sort a report.
const isFirstParty = (host) => host === pageHost || pageHost.endsWith('.' + host.replace(/^\./, '')) || host.replace(/^\./, '') === pageHost;
async function snapshot(context, page, requests) {
const cookies = await context.cookies();
let storage = { local: [], session: [] };
try {
storage = await page.evaluate(() => ({ local: Object.keys(localStorage), session: Object.keys(sessionStorage) }));
} catch { /* storage blocked or page gone */ }
return { cookies, storage, thirdParty: [...new Set(requests)].filter((r) => !isFirstParty(new URL(r).hostname)) };
}
function report(title, snap) {
console.log(`\n== ${title}`);
if (snap.cookies.length === 0) console.log('cookies: none');
for (const c of snap.cookies) {
const days = c.expires > 0 ? Math.round((c.expires * 1000 - Date.now()) / 86400000) + ' days' : 'session';
const party = isFirstParty(c.domain.replace(/^\./, '')) ? '1st' : '3rd';
const flag = KNOWN.some((re) => re.test(c.name)) ? ' <- analytics/ads name' : '';
console.log(`cookie ${c.name.padEnd(12)} ${party} ${c.domain.padEnd(12)} ${days.padEnd(9)}${c.httpOnly ? ' httpOnly' : ''}${flag}`);
}
console.log(`localStorage: ${snap.storage.local.join(', ') || 'none'}`);
console.log(`sessionStorage: ${snap.storage.session.join(', ') || 'none'}`);
if (snap.thirdParty.length === 0) console.log('third-party requests: none');
for (const r of snap.thirdParty) console.log(`request ${r.length > 110 ? r.slice(0, 110) + '...' : r}`);
}
async function visit(clickReject) {
const browser = await chromium.launch();
const context = await browser.newContext(); // fresh profile: no earlier consent
const page = await context.newPage();
const requests = [];
page.on('request', (r) => { if (/^https?:/.test(r.url())) requests.push(r.url()); });
try {
const resp = await page.goto(url, { waitUntil: 'load', timeout: 30000 });
if (!resp || !resp.ok()) throw new Error(`HTTP ${resp ? resp.status() : 'no response'}`);
await page.waitForTimeout(waitMs);
if (!clickReject) return await snapshot(context, page, requests);
const button = page.getByRole('button', { name: /reject|decline|refuse|deny|necessary only/i }).first();
if ((await button.count()) === 0) return { noButton: true };
requests.length = 0; // from here on, count only what the page sends after the click
await button.click();
await page.reload({ waitUntil: 'load' }); // a second page view, as the visitor reads on
await page.waitForTimeout(waitMs);
return await snapshot(context, page, requests);
} finally {
await browser.close();
}
}
try {
console.log(`page: ${url} (checked ${new Date().toISOString()})`);
report('before any click', await visit(false));
if (doReject) {
const after = await visit(true);
if (after.noButton) console.log('\n== after Reject: no reject button found on the first layer');
else report('after clicking Reject and loading the next page', after);
}
} catch (e) {
console.error(`could not check ${url}: ${e.message.split('\n')[0]}`);
process.exit(2);
}
It exits with code 1 without a URL and code 2 when the page fails to load. So it can run on a schedule and fail loudly, a setup our piece on scheduled scripts covers.
Know its blind spots. It finds Reject buttons in the page itself, not inside an iframe. Its first-party test is rough, so a request to your own cdn. subdomain shows up as third-party.
The script cannot see fingerprinting or judge purpose. If your banner changes by country, run it from a connection in each.
What this means for you
The decision is whether the analytics and embeds you run are worth a banner. A site with no banner needs to keep everything else off the page until a visitor agrees. These steps follow the sources:
- List what loads before any click. Run the script, or open your browser’s developer tools in a private window, and write down every cookie, storage key and outside request.
- Give each item one purpose. If you cannot say why something is strictly necessary for the page the visitor asked for, treat it as needing consent. Several purposes on one item means consent unless every one of them is exempt.
- Decide on analytics by country. For EU visitors, either ask for consent or meet a national exemption such as the CNIL’s 13-month conditions. For UK visitors, first-party statistics can run with a notice and a free opt-out.
- If you keep a banner, build it to the rules. Put Reject beside Accept, skip pre-ticked boxes and load nothing before the click. Add a link on every page to change the choice.
- Shorten preference cookies. For EU visitors, keep a language or theme cookie to the session or a little longer, as the WP29 opinion describes. For UK visitors, mention it and offer a free way to object.
- Re-run the check after every new tag or plug-in.
The rules may move again. The European Commission proposed its Digital Omnibus on November 19, 2025, which would rewrite parts of the EU’s data rules. On October 24, 2026, Parliament’s file showed it awaiting a committee decision, with no position adopted. Until a change passes, Article 5(3) is the text to build to.
How we made this
On October 24, 2026, we read the legal texts on legislation.gov.uk: Article 5(3) of the ePrivacy Directive, the UK’s new regulation 6 and Schedule A1, and GDPR Articles 4(11) and 7(3). The same day we read the EDPB, WP29, CNIL and ICO documents listed below, the Court’s Planet49 press release and Parliament’s procedure file. We wrote the script above and ran it with Playwright 1.63 and Chromium; the results we report come only from two sample sites we built ourselves. We did not test other companies’ sites or any consent management product, and none of this is legal advice.
Sources
- Directive 2002/58/EC, Article 5, as amended by Directive 2009/136/EC (legislation.gov.uk copy of the EU text as of December 19, 2009).
- Privacy and Electronic Communications Regulations 2003, regulation 6 and Schedule A1, substituted and inserted by the Data (Use and Access) Act 2025 from February 5, 2026.
- Regulation (EU) 2016/679 (GDPR), Article 4 and Article 7, as adopted.
- European Data Protection Board, Guidelines 2/2023 on the technical scope of Art. 5(3) ePrivacy Directive, version 2.0, adopted October 7, 2024.
- European Data Protection Board, Guidelines 05/2020 on consent, version 1.1, paragraphs 39 to 41, 86 and 113 to 114.
- European Data Protection Board, Report of the work undertaken by the Cookie Banner Taskforce, adopted January 18, 2023.
- Article 29 Working Party, Opinion 04/2012 on Cookie Consent Exemption (WP194), adopted June 7, 2012.
- Court of Justice of the EU, press release 125/19 on Planet49, case C-673/17, October 1, 2019.
- CNIL, Sheet 16: Use analytics on your websites and applications, GDPR developer’s guide, June 11, 2020.
- Information Commissioner’s Office, What are the exceptions?, guidance on storage and access technologies.
- European Parliament Legislative Observatory, procedure 2025/0360(COD), Digital Omnibus.
- Chrome for Developers, cookie expiration capped at 400 days.