My own consent system: $0 and two weekends

Published:

A do-it-yourself consent system: zero dollars and two weekends. Not because twenty bucks for a CMP hurts, but because I wanted the decision to come from the need and not the other way round. It also looked like a fun challenge - bolting consent onto a setup with no gtag and no web GTM. Here is what worked, what did not, and which of my own questions I closed along the way.

Consent mode has always been an awkward topic for me, and it still is, because it sits somewhere between marTech and law. Even when you understand how the consent signal travels to a vendor on the technical level, you’re never 100% sure your implementation is correct, because you’re not a lawyer. This article is mostly about my own under-the-hood experience: how I set up everything around consent, privacy, cookies, privacy policies and all of that. One disclaimer up front: I’m not a lawyer. Everything below is my experience and my reading of the rules, not advice to act on. Check it against your own case, and better yet, ask a proper lawyer.

I’m a worrier by nature, and even with zero traffic, back when the site was still being built, I already caught myself thinking I’d sleep better knowing I break nothing. Nobody is likely to go hunting for legal gaps in a home blog, but besides sleeping well I also wanted to tick a professional box next to the achievement “figure out consent from A to Z”.

Before we get into rules and signals, let me show what came out of it. The banner has two layers: the first one is a short explanation and three equal actions, the second one is categories with toggles and a list of cookies grouped by whoever sets them, each with a description and a lifetime. Everything below is about why it looks exactly like this.

First layer of the consent window: the heading “The glasses always come off” with drawn spectacles, then the text - my analytics and cookies help me see which posts are useful, what to write next and what needs fixing, details of every cookie you get live under “Manage preferences”, no locked doors, the content is fully open either way. At the bottom two green buttons, “Accept all” and “Reject all”, and “Manage preferences” in gold on the right, with a cross in the top corner.

Second layer of the consent window, “Behind the curtain”: three categories with toggles - Necessary on, Preferences off, Statistics on. Under Statistics two cookie tables with the columns name, description and expiration: GOOGLE ANALYTICS - _ga and _ga_CVFELT5065, both 400 days; THIS SITE - _ozb at 30 days and _ozu at 400. At the bottom the buttons “Accept all”, “Reject all” and “Save choice”.

Do I worry about data completeness?

Honestly, I used to worry a lot. These days the answer is closer to “not really”. Hand on heart, it still makes me uncomfortable to realise that analytics or ad platforms capture less than 100% of the signals: an ad blocker here, a rejected banner there, something else on top of that… I try not to think about it.

Back when I was setting consent up for the first time, I kept looking for ways to game the system. I read about cookieless pings, fingerprints, storing identifiers somewhere other than the user’s browser… In short, I wanted to keep getting signals from users even when they’d explicitly said no.

These days I’d rather act cleanly and not argue with my own conscience. For my own project I made the call: we keep it legit, with one grey area around cookieless pings. We respect the user’s decision on privacy and we keep the privacy policy up to date and written in human language, so the user isn’t confused even more (that one, by the way, is a requirement of article 12 of the GDPR: write in “clear and plain language”).

Whose laws do I fall under?

From the very beginning I planned to write in two languages, my native one and English. The second one is an obvious nod to worldwide reach, and my potential audience is predictably scattered all over the planet, though the focus is still mostly on America and Europe. And let’s be honest, professional terminology gets googled not only by native English speakers, I have that feeling.

Europe has the strictest requirements: until you give consent, the site has no right to record anything about you. America is simpler here. By default you’re allowed to be tracked, but at any moment you can tell them to stop.

So to begin with I split the world into three rough jurisdictions:

  1. strict (opt-IN);
  2. soft (opt-OUT);
  3. free.

The first one is (mostly) the European zone, the second one is America. The rest of the world is the fallback.

I went a bit overboard here and decided to make your life easier too. For convenience I collected all the jurisdictions in one table. If you spot an error or something inaccurate, please tell me.

So you should start from where your target audience, your visitors, come from. The mode has already been covered above: strict means forbidden until consent is given, soft means allowed until the user objects. The last column matters too, because every jurisdiction has its own quirks.

Jurisdiction Law Mode What it means for the site
EU, 27 GDPR + ePrivacy 5(3) through national laws strict banner required; before the answer you may neither write nor read; proving consent is on me
Norway, Iceland, Liechtenstein the same, through the EEA agreement strict all the same
China PIPL art. 13 strict banner required; legitimate interest does not exist in the law at all
India DPDPA art. 4 and 7 strict banner required; “legitimate uses” is a closed list, and analytics is not on it
Turkey KVKK + the cookie guidance strict banner required; the consent text and the notice have to be separate
Quebec Law 25, art. 8.1 strict for profiling banner needed if we profile; identification and profiling are off until the answer
UK * PECR reg. 6 + Schedule A1 §5 soft, strict for me the first-party statistics exemption exists, but it falls away once data goes outside
Japan * APPI art. 31 soft, strict for me collecting is fine, sending is not: a transfer to a third party needs consent
Switzerland revFADP art. 30 and 31 soft banner not required; an explicit “no” has to be honoured
Montenegro Electronic Communications Act art. 124(3) soft needs a visible way to refuse and an explanation of the purposes
Brazil LGPD art. 7(IX) + ANPD guidance 2024 soft for statistics written interest assessment; profiles and ads only with consent
South Korea PIPA art. 15(1)(6) soft, but shaky the interest must “clearly outweigh” the person’s rights, a higher bar than in Europe
Nigeria NDP Act 2023 + GAID art. 26 soft written assessment; profiling and third-party ads on this basis are banned
Saudi Arabia PDPL art. 6 as amended 2023 soft assessment required, sensitive data excluded
Indonesia UU PDP art. 20 soft balancing test
Thailand PDPA art. 24(5) and 32 soft an objection has to be honoured, and proving the interest is on me
Canada, federal PIPEDA, Schedule 1, cl. 4.3.6 soft implied consent works, meaning a clear notice
Australia ** Privacy Act 1988, APP 3 does not apply right now the 3M AUD turnover threshold exempts me, and that is my case
South Africa POPIA art. 11 soft the right to object on personal grounds
USA 20 state laws; California - CCPA § 1798.135 soft plus a signal the blog does not reach the 100,000 state consumers threshold; GPC has to be honoured in 12 states regardless of it
Ukraine Law No. 2297-VI free no banner shown; the way to object is a request to the owner, not a button

* The UK and Japan are soft by law, but in my case it works out strict: the first-party statistics exemption falls away the moment data leaves for someone else, and mine leaves for Google.

** Australia does not apply right now: the 3M AUD turnover threshold exempts me. The exemption is being removed on 10.12.2026, after which the mode becomes soft.

There are a lot of jurisdictions and a lot of acts, but they are mostly about the same thing. Work out the logic of your site and your consent banner for the three categories (strict, soft, free) and that should be entirely enough. At least that is the road I picked for myself and for this blog.

Do I need to buy a CMP?

At zero traffic we are of course not talking about four bags of money (a local meme), the price is 10-20 dollars for some minimal paid plan. But the main question is not even about money.

Everything in my blog revolves around the DIY concept, and I am definitely not a fan of hanging 100500 pieces of js code of unclear origin onto a site (remember, I deliberately gave up web GTM too). It is not even about the scale of this blog as a project: at my main job we are also building an internal CMP. Why? Because off-the-shelf solutions are not flexible and often come loaded with all sorts of junk sold as extra features. So we are planning to move off an Enterprise solution onto our own, in-house one soon.

Back to the blog. If I put it as a list, here are the reasons that pushed me towards my own solution:

  1. the DIY concept, as the most flexible thing to customise later;
  2. one less subscription, saved money;
  3. the banner script of a ready-made CMP weighs as much as 5 of my pages (roughly);
  4. objectively, I still do not get what technology CMP companies charge for, other than it coming out of the box;
  5. the option to build a custom system of banner revisions and a signature store for every individual user (an interesting one, more on it later);
  6. it is not obvious at first glance how to integrate a ready-made solution with pure server-side tracking on the site;

If you thought I went through some agony of choice, no: the decision took about three minutes and went to the homemade one. So the consent system cost 0 dollars in total, and development and rollout took about 2 weekend days.

Where the signal comes from and how it reaches the vendor

What follows is a bit of reference material, but it is the important kind worth paying attention to (in my opinion). Also keep in mind that my particular tracking setup differs from 99% of sites: the gtag mentioned below does not exist on mine.

The consent signal is first created by that same gtag, the script that, in simple words, holds up all the tracking of Google services, builds the payload and sends hits from the browser.

Then, when the user interacts with the banner, a new signal is created. These signals travel from the user’s browser to the vendor inside the payload, in four parameters:

  1. gcd (Google Consent Data)
  2. gcs (Google Consent Status / State /)
  3. npa (Non-personalized Ads)
  4. dma (Digital Markets Act)

Worth noting: Google published an official expansion only for NPA, the rest are pure guesses from the community.

GCD

The most informative parameter, its values look like 13t3t3t3t5l1 (just an example). Like the rest, it is passed with every event and carries the user’s current consent state. Inside it there is:

  1. the format version
  2. four pairs of characters, one per consent type
  3. 3 service characters

I am not even going to suggest memorising it, but if you are curious, have a closer look at the cheat sheet I put together for you. I do not keep this in my head myself, and you probably do not need to either.

A reference card: how the gcd value is built. At the top the example 13t3t3t3t5l1, split into twelve characters in boxes - the format version, four pairs by consent type (ad_storage, analytics_storage, ad_user_data, ad_personalization) and three service characters. Below: a letter is a number from 0 to 63, its position in Google’s alphabet; state codes - 0 nothing is known, 1 not set, 2 denied, 3 granted; the pair 3t taken apart by bits - type delegation, implicitly inferred state, declared before interaction, site default, the person’s answer; what the three service characters are for; and the seniority rule: the person’s answer beats the site default, the default beats the implicitly inferred state.

If it is still unclear, you can try the decoder: you paste a gcd value and get an explanation of the user’s current consent state in human language.

GCS

I do not really see much point in gcs existing at all, since it is fully derived from the value of gcd and carries no additional information. The only use for it I have seen is in custom tag and variable templates in the server container.

Still, let us look at what gets passed there. Nine unique values in total:

Value ad_storage analytics_storage
G111 granted granted
G110 granted denied
G11- granted not set
G101 denied granted
G100 denied denied
G10- denied not set
G1-1 not set granted
G1-0 not set denied
G1-- not set not set

Nine is exactly what you get: two consent types, three states each - granted, denied, not set.

One thing is worth knowing, and other write-ups do not have it. The “not set” state (the dash) and the “denied” state (the zero) do not come from where you would think. If a permission was inferred implicitly, gcs shows a dash, that is, “nothing is known”. And if a denial was inferred implicitly, it shows a zero, that is, “denied”. Asymmetric: an implicit yes hides, an implicit no gets announced. Why it works that way the code does not say, it just does.

But again, this is not something worth memorising.

NPA & DMA

NPA, Non-personalized Ads, is the only parameter that works inside out, in reverse. It is an advertising parameter telling Google to show ads blind, that is, without using what it already knows about the user. A one in the value turns this mode on, a zero is the default. So with npa = 1 the following will NOT work:

  1. audience targeting
  2. demographic targeting
  3. matching by the user’s past behaviour (search queries, visited sites, geo history and so on)
  4. Google does not use new information about the user to pick ads

Worth saying as well that this parameter brings nothing new: everything is already baked into gcd.

DMA, Digital Markets Act, is a parameter named after a law, not after a technology. It concerns neither me, nor you, nor even your users. It concerns the big “players”:

  1. Google
  2. Apple
  3. Meta
  4. Amazon
  5. Microsoft;
  6. ByteDance (TikTok);
  7. Booking.com

This is a European competition act, and the gist of it is: a large platform may not stitch data about a user across its own services without that user’s consent. For example: you searched for “car rental in Barcelona”, and an hour later you opened YouTube. Without your consent Google has no right to show you rental ads there, because that would mean putting together what it knows about you from two of its own services. With consent it does. That is exactly where the ad_user_data and ad_personalization flags came from.

dma = 1 is set when (at the same time):

  1. the consent system has declared that TCF is in use OR advertiser consent mode;
  2. the consent system has reported that it considers the traffic to fall under GDPR;

Mine is always zero, because the first condition never holds.

To sum up, only two of the four parameters stand on their own: gcd and dma. gcs and npa can be derived from the value of gcd.

How are users who hit Reject All tracked?

Some notes here on so-called cookieless hits. Essentially these are the same requests as with consent given, except that they carry no persistent information (user_pseudo_id, session_id and so on) that would let you tie the requests together and associate them with a particular user.

A word on IP separately: not sending it is impossible in principle, because it is part of any http request. But sending != automatically recording. Which is what I do: if the user has not consented yet, the browser sends the IP, but on my side I simply do not read it and do not write it down.

Six parameters also do not make it into a cookieless ping:

Parameter What it is
sid session id: the second the session started
sct session count for this particular user
seg whether the session was engaged
_fv first visit: there was no session cookie for this recipient yet
_nsi new to site: the browser identifier was created from scratch
_ss session start

All six are derived from a cookie, and when consent is refused there is no cookie. Sending them would mean inventing a session for a person I do not recognise.

Let me put a separate emphasis on cid. That is client_id (user_pseudo_id), the browser identifier. Users are counted by it. With consent this identifier gets written into a cookie. When there is no consent, we cannot write them, yet they are generated anyway. There is a catch: this identifier is generated anew on every page load. Which makes stitching one person’s events into a single user journey impossible. A second catch: within one page load the identifier stays the same, so events happening inside a single page load share a common, but still temporary cid (client_id or user_pseudo_id).

Google and its grey area

When a person hits “Reject” in the cookie banner, gtag does not actually go quiet. The logic on this site, described above, is built on the very same mechanism: events are sent somewhat trimmed down, and cookies are not written. Google does not hide this approach at all and writes it in plain text:

When consent is denied, consent state and measurements without cookies are sent

Google’s defence of that decision rests on three points:

  1. “we write nothing to the user’s device”;
  2. “the IP is needed to determine the user’s geo. We do not store it afterwards”;
  3. “the person cannot be identified from the data that is sent anyway”;

It even looks safe at first. But ePrivacy was not written only about storing cookies. It, and specifically article 5(3) of Directive 2002/58/EC, is about two different actions: writing something to the user’s device and taking something from the device. Both are forbidden, and one of the two conditions is enough.

That is where the construction hides. “We write nothing” calmly closes the first action while flatly ignoring the second. And about the second one the regulators have been unambiguous. If the information “is sent back over the network to a server”, that is access, and the fact that it was computed right on the device changes nothing.

Put differently: the law looks not at where the computation happened, but at whether the data left the device. A script can measure the screen resolution in the browser without calling anywhere, and as long as the number stays there, there are no questions. The moment it goes to a server, that is already “gaining access”, and article 5(3) applies.

A separate word on the IP. “We do not store it” is an answer to a different question. The law asks precisely about obtaining. You can store nothing, but access has already happened.

“The person cannot be identified” - here the wording matters: “cannot directly identify an individual”. The key word is “directly”. Look, in its own documentation Google lists what goes into the ping: user agent, screen resolution, IP, referrer, time and a random number for every page load. Look closely and that set is enough to fingerprint a device. And Google does not say it does anything like that, it says a person cannot be identified directly. Formally it is not lying, because each hit parameter on its own really means nothing. Their value appears when they arrive together.

At the same time, no regulator has so far called this scheme a violation. One text of the law, article 5(3) of ePrivacy, two readings: the regulator’s and Google’s. And nobody has yet gone and tested whose interpretation is the more correct one.

So I have just taken apart the arguments I am standing on myself. My grey area is borrowed from Google, and I take the risk knowingly.

So when a user refuses, he still gets tracked on my site. Yes, with limits and with no way to recognise him tomorrow, but tracked. This is probably where my conscience draws the line, and I feel better saying it out loud than keeping quiet about it.

What are the requirements for a banner?

Back when our team was setting up consent mode at work with a pricey CMP, I remember that endless stream of questions - “what about this”, “how about that” - about what has to be shown in the banner itself, and in what form.

Let me go through the list of questions I asked myself and thought I had to close. The answers are right under the spoilers:

How OK is it to have just a small banner saying "We collect your data" and a single OK button as consent?

Not OK. The EDPB (European Data Protection Board) writes it plainly: the absence of a way to refuse on any banner screen that has a consent button is a direct violation. So if there is an “OK” button, there has to be a “NOT OK” one on the same level. And both have to be equal and equally attractive to click.

Are there fixed requirements for the text on buttons and links?

There are none. There is a ban on misleading the user. The EDPB deliberately refuses to set any visual standards for banner design, and if it comes to it, every case is considered individually. The text has to be readable and unambiguous

Are there requirements for the size of the banner, and can it be hidden without giving any answer at all?

There are no size requirements, half the page if you like. But the absence of a clear expression of will is unambiguously equal to a full refusal.

Can you block the content with the banner, build a sort of Cookie Wall and not let the user in until they answer?

The EDPB sees an element of coercion in this, and therefore does not treat such answers as a genuine expression of will. So I would advise against risking it if a noticeable share of your traffic comes from European countries.

Is detailed information about every cookie mandatory?

The list of cookies itself is mandatory, but keeping the full description of each individual cookie in the banner is not (it can be moved to the privacy policy). Keeping the list and justifying every single cookie is the site owner’s duty. What matters: regulators do not expect a perfect list from you. They themselves admit cookies change all the time. What they expect is discipline in working with the list and keeping it up to date.

What has to be in the description of each cookie?

Name, who sets it (vendor), what for, how long it lives, the legal basis. If a third party receives data from the cookie, then a link to its data processing policy is worth adding on top.

Are there requirements for cookie categories - names, how they are cut up and so on?

Regulators do not define such a thing as categories at all. What is defined is granularity: the user must be able to, say, refuse advertising but allow analytics to track them. Artificially inflating the necessary cookies category, stuffing analytics cookies in there for example, is categorically forbidden.

How do you keep the cookie list up to date?

The honest answer: keeping it complete and up to date at all times is impossible, and regulators openly admit it. Cookies can change unilaterally (on the vendor’s side), and the other party is not obliged to monitor changes in vendor policies every day. That is why site owners are expected not to have a perfect list, but to understand that the list is alive and gets updated from time to time.

Can you make one universal banner for all jurisdictions at once?

The banner can be the same, but the logic cannot. The most basic example: Europe first forbids and then asks, America is the other way round, it first allows and then may ask. On top of that America has the GPC signal, which as of September 2026 must be honoured by 12 states: California, Colorado, Connecticut, Delaware, Maryland, Minnesota, Montana, Nebraska, New Hampshire, New Jersey, Oregon and Texas. Which means that in “soft” mode the banner stops being the main thing. The main thing becomes the Sec-GPC: 1 header from your browser.

What if the browser says "no" and the user clicks "yes"?

The GPC signal is generated at the browser level and, logically enough, applies to every site the user visits. The Californian rules settle it unambiguously: first honour the refusal, and only then may you tell the user about it and ask for their consent again. Which means simply clicking “Accept all” is not enough - a positive GPC signal is only overridden by informed consent, given after the user has been told that their browser is asking for the opposite.

So the main question here is not “how to compile the list”, but “where to take it from”.

The list comes from the tools, which is obvious: do you run Google Ads? You go to Google’s policy and take all the cookies that relate to advertising. That way a new cookie cannot appear for your users from a service you never installed. But there is a limit here: such a description gives you the full list of sources, yet does not guarantee the full list of cookies. A vendor can quietly add a new one, and you will not catch that in time (you are not going to visit their data processing policy page every day).

I solved this with one cookie dictionary and three checks. The dictionary is a file describing every cookie: name, category, who writes it, who the data goes to, how long it lives. From there the list is pulled into the second layer of the cookie banner, and into the privacy policy page as well. Going out of sync is ruled out, because there is a single source.

Then come the checks against fact. Each answers its own question.

Check No. 1: the code, or what can be set at all. On every site build the check reads the code and assembles the list of cookies that could be set. Every cookie name from that list has to be found in the dictionary, and the other way round: if a record exists but there is no such cookie in the code, we get a red check.

Check No. 2: the browser, or what got set not from the server. A real browser run (using Playwright) over the built static files: pages are served by a file server, and my Cloudflare Worker is obviously unavailable at that point. So what gets recorded is exclusively what the browser sets, not what the server does.

Check No. 3: the raw data warehouse, cookie names (without values) that actually got set for live people. And cookies from there have to be in the dictionary. An unknown cookie shows up in the warehouse and the next run of the warehouse checks lights up red.

None of these checks is complete on its own: the code does not know what actually ran, the browser only sees the scenarios written in advance, and the warehouse shows the list after the fact. Together they cover three different blind spots, and as long as there are no foreign scripts on my site, these three will be enough for me.

One last thing: I even have versioning, both of the cookie list and of the banner with the policy. If I make a change to the current cookie list, the version of the banner and of the policy change automatically, since the cookie list is an inseparable part of them.

Three words that keep coming up below. So we do not get confused:

  1. Signature - one answer from one person: what they allowed, when, from which page and in which mode. Sits in its own file, and it is the evidence;
  2. Revision - the state of what was shown to them: the cookie list, the consent window together with its look and buttons, the text of the privacy policy. Any of that changed and a new revision appears;
  3. Fingerprint - eight characters naming a revision. A signature points at revisions by fingerprints rather than retelling their content.

What the law requires

Article 7(1) of the GDPR (in more human language): the data owner has to be able to prove that a particular person gave consent. The law does not say by what means.

The EDPB in its guidelines on consent, clause 107: “The GDPR does not prescribe exactly how this must be done”. In the same place there is the answer to how long this evidence should be kept: as long as the data is processed, the duty to show the consent for processing that data stands too.

The CNIL additionally lists suitable options:

  1. A code fingerprint with a timestamp;
  2. A screenshot with the banner;
  3. External third-party audits;
  4. A settings history in the CMP;

There is nothing like a personal “my data” page in that list, but I still wanted to let everyone look at their own signature.

Revisions of the banner and the privacy policy

A record without the version of the text shown to the person proves nothing, in my opinion. That is why every signature in my system carries three revision fingerprints:

  1. the version of the cookie dictionary;
  2. the version of the banner;
  3. the version of the privacy policy

two variants each, one per language. A fingerprint is eight characters computed from the text itself, one way only (a hash). The revision itself is stored alongside it, otherwise you end up with a key and no lock.

The privacy policy version got updated, a new record appeared in the warehouse, and every signature after that will point at the new revision. Same with the cookies and the banner. Worth noting separately: since the cookie list is part of both the banner and the policy, updating the list automatically versions all three entities at once.

The log stores signatures. On the user’s side their subject identifier (subject_id) is kept in the _ozr cookie with Path=/_consent/, meaning the browser only writes it for that service address. Outside that path the cookie does not exist at all. So this segmentation rules out subject_id ever landing in analytics data.

The consent log store, the records section: a list of folders named by subject numbers - 03038ae5-8be6-4829-9af3-c7288ac92938, 0682db9d-d89e-4561-99a8-40df92c0eab2 and so on, among them e730a02c-379a-46a9-b77c-672248466619 from the previous screenshot. One folder is one subject, and their signatures sit inside it.The consent log store, the records section: a list of folders named by subject numbers - 03038ae5-8be6-4829-9af3-c7288ac92938, 0682db9d-d89e-4561-99a8-40df92c0eab2 and so on, among them e730a02c-379a-46a9-b77c-672248466619 from the previous screenshot. One folder is one subject, and their signatures sit inside it.

The signature itself is nineteen fields:

Field What it is
record the number of this signature
subject the person’s number from the _ozr cookie
at when the signature was created
decided_at when the person clicked the button
ts the same moment, as a number
button which button exactly they clicked
origin taken from the click or caught up from the cookie
action the outcome in one word: all, partial or refusal
granted what they allowed
denied what they rejected
mode the mode: strict, soft or free
country the country the request came from
is_eu whether the visit falls under European rules
lang the language of the window that was shown
page the page the button was clicked from
cookies_version the fingerprint of the cookie list revision
banner_version the fingerprint of the window revision
policy_version the fingerprint of the policy revision
expires_at the date it stays valid until

On the signature page there is one line that does not exist in the signature at all: Analytics. Many will recognise our good old client_id in it, also known as user_pseudo_id. It is not in the signature itself: there you only get the signature number, the person’s number, the decision, the mode and country, the language, three revision fingerprints and the dates. It is the signature page that shows it, and only at the moment you opened that page. It is needed because a “delete my data” request concerns not the consent log but the event warehouse, and there a person is found by exactly this number and by no other.

The “show my consent record” page: record 3e9a6f4c-5710-4ec0-9c4f-db94ab6e2a3e, subject e730a02c-379a-46a9-b77c-672248466619, decided on 7 September 2026, allowed preferences, statistics and marketing, declined nothing, button accept_all, mode strict (DE), valid to 12 October 2027, window 112cb852 (uk), cookies 520ed672, policy 7a395c17, source taken from your click, analytics 368737993.1785734098. At the bottom: decisions in total 2 and the address to write to if you want this data deleted.The “show my consent record” page: record 3e9a6f4c-5710-4ec0-9c4f-db94ab6e2a3e, subject e730a02c-379a-46a9-b77c-672248466619, decided on 7 September 2026, allowed preferences, statistics and marketing, declined nothing, button accept_all, mode strict (DE), valid to 12 October 2027, window 112cb852 (uk), cookies 520ed672, policy 7a395c17, source taken from your click, analytics 368737993.1785734098. At the bottom: decisions in total 2 and the address to write to if you want this data deleted.

This is the thinnest spot in the whole scheme. The signature number lives in the consent log, the client_id lives in the event warehouse. While they stay apart, the warehouse remains impersonal: just browsers with no people attached. Write those two ids side by side even once and every event becomes an event of a specific person who once clicked a button in my window.

That is why the page “show my consent record” shows both numbers side by side and stores neither. It is the only screen where they meet, and the meeting lasts exactly as long as you are reading that page. The view of this page does not go into analytics either, otherwise I would be doing the very thing I argue against.

How this gets pulled out when needed

The scenario is simple. I get an email asking to show what the user agreed to and when. The email has a copy-paste from the “show my consent record” page. By the signature number I find their file, and in it three fingerprints: of the cookie dictionary, of the banner and of the policy. Then three lookups in the store, one per fingerprint.

The consent log store, the versions section: folders of banner revisions - banner-112cb852-uk, banner-0dec4001-uk, banner-15408399-en and others - and files of cookie dictionary and policy revisions: cookies-520ed672.json at 6.59 KB, policy-7a395c17-uk.json at 13.36 KB. These three fingerprints - 112cb852, 520ed672 and 7a395c17 - are the ones written in the signature above.The consent log store, the versions section: folders of banner revisions - banner-112cb852-uk, banner-0dec4001-uk, banner-15408399-en and others - and files of cookie dictionary and policy revisions: cookies-520ed672.json at 6.59 KB, policy-7a395c17-uk.json at 13.36 KB. These three fingerprints - 112cb852, 520ed672 and 7a395c17 - are the ones written in the signature above.

The policy arrives as full text, with the date the revision first appeared next to it. The dictionary arrives as the list of cookies shown in that revision. The banner arrives as two files: parts.json with the source texts of the window (the markup of both layers, the styling, the images, exactly what the server handed over to build the banner) and banner.html itself, assembled from them.

That last file is the thing everything was done for. It is self-contained: all the styling inside, not a single external reference. It opens without internet, a year later, on someone else’s computer, and shows the same banner with the same buttons and the same cookie table. The site may be rewritten three times by then, and the revision will not care.

And now about what makes this banner.html evidence rather than a picture. The evidence is not the banner itself, but the files next to it and the fingerprint computed from them. Anyone holding these files can recompute the fingerprint and compare it with the number written in the signature. Matches, and it is the same revision. Does not match, and it is the wrong one. Eight characters work here as a revision number, not as cryptographic protection: picking another text with the same short fingerprint is theoretically possible, so the evidence is the bundle “signature plus revision in my store”, not the strength of the number itself.

Instead of a conclusion

So what did I take away from building my own solution and writing this article?

In Europe the focus has to be on two laws, not one: GDPR + ePrivacy. The latter is about the device, both about writing to it (cookies) and about reading some information from it (headers, user agent and so on). The GDPR is about personal data and on what basis we process it.

These are different subjects, and confusing them creates half of the consent arguments in Europe. The rest of the world mostly leans on a single law (mostly, because there are exceptions).

Transparency does not grant rights. Telling people about the collection is a duty under article 13 of the GDPR. Having the right to collect is a separate question of legal basis: consent, contract or legitimate interest. Writing it in the policy and considering the matter closed is the most common substitution. The site honestly wrote that analytics is strictly necessary and calmed down at that. But a line in a policy does not create necessity: at the very first request to prove why analytics is necessary, that defence falls apart.

Of the four parameters that carry the consent signal through the veins of the payload, only two bring “added value”: gcd and dma. gcs and npa are derived from gcd.

Google does not go quiet on a refusal, and neither do I. That “greyness” of mine is borrowed, and I take the risk knowingly. Why? Because no regulator has called it a violation yet. So my conclusion is that for now it is “allowed, but carefully”.

Refusing in the banner has to be exactly as easy as agreeing. That is probably the single most important requirement for a banner, and it is about actions, not aesthetics. One click to agree and three to refuse through the second layer is already a violation. Colour is judged case by case, and regulators explicitly forbid only one thing: a button with unreadable text or outright weak contrast.

Cookie categories (the name of a category, the axis it is cut along) are not defined anywhere. The only thing defined is granularity: there has to be a way to refuse one thing and consent to another.

Four categories (necessary, functional, analytics, advertising) are more of a tradition than a requirement. And it surprises me that this particular set is the one that stuck. Google has three separate signals for advertising:

  1. ad_storage - whether ad identifiers may be written;
  2. ad_user_data - whether my data may be handed to it;
  3. ad_personalization - whether I may be gathered into audiences.

I see no reason to forbid a site from recording the click id I arrived with, and at the same time I do not want to end up in somebody’s audience or hand Google my email, even as a hash. These are three different decisions, and in every banner I have opened they get switched off with one click under a shared name, “marketing”.

The reason, I suspect, is banal: more toggles, lower banner conversion. But nothing stops you from splitting “marketing” into two categories and letting the person choose more precisely. I would have done exactly that if I ran ads (for now I have one solid “marketing”).

Cookie Wall. Blocking the content until the person answers is seen by regulators as coercion: a choice under pressure is not a choice. Although there is no agreement between them here: the EDPB is categorical, while in France the blanket ban was overturned by the Council of State, and cases are considered individually there.

Oh, I have this feeling that even the conclusion needs a conclusion of its own, and there is still more in my head I would like to add. But I will stop here. All in all, I am very happy with the result of this work and with how deep I got to dig. If you have questions or suggestions, please use the button below.

Write to me - I read everything and reply to everyone.

Or just write to hello@ozthewizard.com