Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

SEO & Digital Marketing  Module 11 – Analytics and measurement

Consent, cookies and privacy-safe tracking

Consent mode defaults, a banner where Reject is as easy as Accept, what DPDP, GDPR and ePrivacy ask of marketers, and measurement without hidden tracking.

  • Intermediate
  • 25 minutes
  • Examples run with Node.js 24.21.0, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free lesson, Pro answers: 25 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Implement consent mode with denied defaults set before any tag sends data, and an update call for each banner choice
  • Design a cookie banner on which Reject is as easy as Accept, with nothing pre-ticked and a way to withdraw
  • Explain what India's DPDP Act and Rules, the GDPR and the ePrivacy Directive ask of marketers, and when the DPDP duties apply
  • Set first-party cookies with Secure, HttpOnly, SameSite and the shortest workable lifetime
  • Plan measurement that survives refusals with first-party data instead of hidden tracking

Before you start

What you’ll learn · Before you start

What you will learn

  • Implement consent mode with denied defaults set before any tag sends data, and an update call for each banner choice
  • Design a cookie banner on which Reject is as easy as Accept, with nothing pre-ticked and a way to withdraw
  • Explain what India's DPDP Act and Rules, the GDPR and the ePrivacy Directive ask of marketers, and when the DPDP duties apply
  • Set first-party cookies with Secure, HttpOnly, SameSite and the shortest workable lifetime
  • Plan measurement that survives refusals with first-party data instead of hidden tracking

Before you start

On this page

Pune Bakes added a cookie banner for its customers in Europe, and in the first week 412 visitors pressed Accept while none pressed Reject, because there was no Reject: only Accept all and a small Settings link. A banner like that does not collect consent; it collects clicks on the one button offered.

Consent is where marketing, law and code meet, and each fails without the others. By the end of this page you can set up consent mode so that Google’s tags know each visitor’s choice, design a banner on which refusing is as easy as agreeing, explain what India’s DPDP Act and Rules, the EU’s GDPR and ePrivacy Directive and the UK’s rules ask of marketers and from when, set your own cookies safely, and plan measurement that still works when people say no.

consent mode: Google's way for a site to pass each visitor's consent choices to its tags: a default state set before any measurement, then an update when the visitor chooses. The site itself must still obtain the consent. is how a site passes a visitor’s choice to Google’s tags, which then adjust what they store and send. It has seven consent types; four of them matter for measurement and ads (Consent mode overview):

Consent type What it controls
ad_storage Storage for advertising, such as cookies
ad_user_data Whether user data may be sent to Google for advertising
ad_personalization Whether ads may be personalised
analytics_storage Storage for measurement, such as how long a visit lasts
functionality_storage, personalization_storage, security_storage Storage for the site’s own functions, personal choices such as suggestions, and security such as sign-in checks and fraud prevention

Google is plain about who does the asking: the site is “responsible for obtaining users’ consent”, and consent mode only carries the answer. Its guide sets two commands (Set up consent mode). The default runs before any command that sends measurement data, such as config or event, may be scoped to regions with ISO 3166-2 codes, and may give the banner time to answer with wait_for_update, 500 milliseconds in Google’s example. The update runs “as soon as a user interacts with your consent management solution”, for any change, granted to denied included. Google adds that “the order of the code here is vital”. For visitors in the European Economic Area, Google requires sites that use its tags for measurement, ad personalisation and remarketing to collect consent and pass the signals on, and it added ad_user_data and ad_personalization to consent mode for this (EEA update).

The program replays three setups of the bakery’s pages with a stand-in for the tag’s queue, which follows those rules. Run it, then press Edit and move the default in setup B to the top of its list:

Three consent setups: when the defaults run, and what a visitor starts with Python · consent/consent_calls.py
"""Replay three ways of setting up consent mode on the bakery's pages, with a stand-in for the Google tag's queue.

The rules come from Google's guide "Set up consent mode on websites": the default command runs before any command that
sends measurement data (config or event), "the order of the code here is vital"; a default with a region applies to
visitors there, one without a region to everyone else, and the more specific region wins; by default no consent mode
values are set; EEA traffic also needs ad_user_data and ad_personalization ("Updates to consent mode for traffic in
EEA"). This is a model of those rules, not Google's code. The bakery and its tag id are fictional.
"""
EEA_UK_CH = ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT",
             "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE", "IS", "LI", "NO", "GB", "CH"]
ALL_DENIED = {"ad_storage": "denied", "ad_user_data": "denied", "ad_personalization": "denied",
              "analytics_storage": "denied"}

SETUPS = {
    "A, defaults first": [
        ("consent", "default", {**ALL_DENIED, "region": EEA_UK_CH, "wait_for_update": 500}),
        ("config", "G-PB5678"),
        ("event", "page_view"),
    ],
    "B, defaults after config": [
        ("config", "G-PB5678"),
        ("consent", "default", {**ALL_DENIED, "region": EEA_UK_CH, "wait_for_update": 500}),
        ("event", "page_view"),
    ],
    "C, defaults without the two newer types": [
        ("consent", "default", {"ad_storage": "denied", "analytics_storage": "denied", "region": EEA_UK_CH}),
        ("config", "G-PB5678"),
    ],
}


def state(defaults, country):
    """The consent state a visitor from `country` has: a default for their region wins over one without a region."""
    chosen = {}
    for settings in sorted(defaults, key=lambda d: "region" in d):  # region-less first, so a regional one overrides
        if "region" not in settings or country in settings["region"]:
            chosen.update({k: v for k, v in settings.items() if k not in ("region", "wait_for_update")})
    return ", ".join(f"{k}={v}" for k, v in chosen.items()) or "no consent values set"


for name, queue in SETUPS.items():
    print(f"Setup {name}")
    defaults_seen = []
    for command in queue:
        if command[0] == "consent" and command[1] == "default":
            defaults_seen.append(command[2])
        elif command[0] in ("config", "event"):
            when = "after the defaults" if defaults_seen else "BEFORE any default: consent defaults won't work"
            print(f"  first measurement command, {command[0]} {command[1]}: {when}")
            break
    for country in ("DE", "IN"):
        print(f"  a visitor in {country} starts with: {state(defaults_seen, country)}")
    missing = [t for t in ALL_DENIED if all(t not in d for d in defaults_seen)]
    if defaults_seen and missing:
        print(f"  missing for EEA traffic: {', '.join(missing)}")
    print()

Output

Setup A, defaults first
  first measurement command, config G-PB5678: after the defaults
  a visitor in DE starts with: ad_storage=denied, ad_user_data=denied, ad_personalization=denied, analytics_storage=denied
  a visitor in IN starts with: no consent values set

Setup B, defaults after config
  first measurement command, config G-PB5678: BEFORE any default: consent defaults won't work
  a visitor in DE starts with: no consent values set
  a visitor in IN starts with: no consent values set

Setup C, defaults without the two newer types
  first measurement command, config G-PB5678: after the defaults
  a visitor in DE starts with: ad_storage=denied, analytics_storage=denied
  a visitor in IN starts with: no consent values set
  missing for EEA traffic: ad_user_data, ad_personalization

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 consent_calls.py

Setup A is right. Setup B runs the default after config, so the first measurement goes out with no consent state at all, the case Google warns “consent defaults won’t work”. Setup C forgets the two newer types. In all three, a visitor in India starts with no consent values, because the bakery scoped its banner to the EEA, the UK and Switzerland: which regions need a banner is a question for the law of each place, not for the tag. On a site with Tag Manager, Google recommends loading the banner through the container with a consent mode template, which uses the container’s own consent commands rather than gtag('consent', 'update', …).

Checkpoint

Check: Consent mode's defaults and updates

  1. Question 1 of 2 Where must the consent default command run?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Before any command that sends measurement data, such as config or event

    Default first, then the tag's config and events, then the update as soon as the visitor chooses. In setup B the first measurement goes out before any default exists.

  2. Question 2 of 2 The bakery's only default command has a region list of the EEA countries, the UK and Switzerland. What consent state does a visitor in India start with?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: None at all, because no default covers that region

    A default with a region applies there; one without a region applies to everyone else. Which regions need a banner is a question for the law of each place.

Basic mode waits; advanced mode sends measurements without cookies

Consent mode can be set up in two ways, and the difference is what happens before and after a refusal (Consent mode overview):

What differs Basic consent mode Advanced consent mode
Before the visitor chooses Tags do not load; nothing is sent Tags load with denied defaults and send measurements without cookies
After a refusal No data reaches Google, not even pings Measurements without cookies continue: the consent state, a timestamp, the user agent, the referrer, a random value per page load
After consent Tags load and run normally Tags store cookies and send full measurement
Modelling of the gaps A general model A model specific to the advertiser

Step through one visitor’s choice on the bakery’s site, under advanced mode:

Interactive · message by message One visitor's choice, as consent mode carries it Step through a visit from Germany to the bakery under advanced consent mode, from the default to a later change of mind.
One visitor's choice, as consent mode carries it, step 7 of 7. Messages so far: Bakery's page to Visitor in Germany: banner with three buttons; Bakery's page to Google: measurement without cookies; Visitor in Germany to Bakery's page: Reject all; Bakery's page to Google: still without cookies; Visitor in Germany to Bakery's page: analytics only; Bakery's page to Google: analytics with _ga cookie.Visitor in Germanychoice storedBakery's pageanalytics on, ads offGooglebanner with three buttonsmeasurement without cookiesReject allstill without cookiesanalytics onlyanalytics with _ga cookie

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. Defaults before anything. The page runs the consent default first, before the tag's config. For visitors in the EEA, the UK and Switzerland it sets ad_storage, ad_user_data, ad_personalization and analytics_storage to denied.
  2. The banner asks. The banner offers Accept all, Reject all and Choose, as buttons of the same size and weight, and says what each purpose is for.
  3. Measured without cookies. In advanced mode the tags have already loaded. While she decides, they set no cookies and send measurements without cookies, such as the consent state, a timestamp, the browser's user agent and the referrer.
  4. She rejects. She presses Reject all. The banner sends a consent update with all four types denied and keeps her choice in a first-party cookie, so the banner does not ask again on every page.
  5. Nothing more is stored. On the next pages the tags still set no _ga cookie. Had the bakery used basic mode, its tags would not have loaded at all, and nothing would have reached Google.
  6. A change of mind. A week later she opens Cookie settings from the footer, a link on every page, and allows analytics only. The banner sends an update with analytics_storage granted and the three advertising types denied.
  7. Measured with a cookie. Now the tag may store its cookie and send full analytics measurement, while advertising stays off. Withdrawing later is the same update with granted turned back to denied.

Advanced mode exists for modelling: GA4 can estimate the behaviour of visitors who refused from similar visitors who agreed. The bar is high. A property qualifies only with at least 1,000 events a day with analytics_storage denied for 7 days or more, and at least 1,000 users a day with it granted on 7 of the last 28 days, and it models users, sessions and new users, not event counts (Behavioral modeling). The bakery has about 40 sessions a week. It will never reach those numbers, and neither will any site with fewer than 1,000 consenting users a day, so for such sites modelling fills no gaps, and the choice between the modes is only a choice about what to send before and after a refusal.

Checkpoint

Check: Basic and advanced consent mode

  1. Question 1 of 2 In basic consent mode, what reaches Google from a visitor who refuses?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Nothing, not even pings, because the tags never load

    Basic mode sends nothing before or after a refusal. Advanced mode sends measurements without cookies, which can feed modelling.

  2. Question 2 of 2 How many events a day with analytics_storage denied, for at least 7 days, does a GA4 property need before behavioural modelling can start?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 1000 events a day

    At least 1,000 events a day with analytics_storage denied for 7 days or more, and at least 1,000 users a day with it granted on 7 of the last 28 days. The bakery's 40 sessions a week never get there.

A banner on which Reject is as easy as Accept

A banner decides whether consent is real, and European regulators have said what a bad one looks like. The European Data Protection Board’s cookie banner taskforce found that, for “a vast majority of authorities”, a banner without a refuse option on any layer that has an accept button does not give valid consent; that pre-ticked boxes do not lead to valid consent, citing the GDPR’s recital 32, “Silence, pre-ticked boxes or inactivity should not therefore constitute consent”; that a refuse link embedded in text without enough visual support does not either; and that contrast designed to hide the alternative can be “manifestly misleading” (taskforce report). Withdrawal must match: the GDPR says “it shall be as easy to withdraw as to give consent” (article 7(3)), and India’s DPDP Act asks that withdrawing be comparable in ease to consenting (section 6(4)).

The program checks three banner designs against those findings, and measures the contrast of each Reject option with the formula of WCAG 2.2, whose minimum for normal text is 4.5:1 (WCAG 2.2). The designs are in BANNERS at the program’s top. Run it, then press Edit and give banner 2’s Reject the colours of its Accept button:

Three banners against the taskforce's findings Python · banner/banner_check.py
"""Check three cookie banner designs against the practices the EDPB's cookie banner taskforce assessed.

From the taskforce's report (2023): a banner with no reject option on any layer that has an accept button was, for a
vast majority of authorities, an infringement; pre-ticked boxes do not give valid consent (GDPR recital 32); a
reject option whose text has almost no contrast with its button can be manifestly misleading. Withdrawal must be as easy
as giving consent (GDPR art. 7(3); India's DPDP Act s. 6(4)). Contrast is measured with WCAG 2.2's formula; 4.5:1 is
its minimum for normal text. BANNERS holds the three designs, invented for the bakery: the first layer's options, with
their kind and colours, the second layer's boxes as they are shown, and whether a link on every page reopens the choice.
"""
BLUE, WHITE = "#0B57D0", "#FFFFFF"
BANNERS = [
    {"name": "1, accept or go to settings",
     "first_layer": [{"label": "Accept all", "kind": "button", "text": WHITE, "background": BLUE},
                     {"label": "Settings", "kind": "link", "text": BLUE, "background": WHITE}],
     "second_layer": {"analytics": False, "ads": False}, "withdraw_link": False},
    {"name": "2, reject in faint grey, boxes ticked",
     "first_layer": [{"label": "Accept all", "kind": "button", "text": WHITE, "background": BLUE},
                     {"label": "Reject", "kind": "link", "text": "#E2E2E2", "background": WHITE}],
     "second_layer": {"analytics": True, "ads": True}, "withdraw_link": True},
    {"name": "3, accept and reject alike",
     "first_layer": [{"label": "Accept all", "kind": "button", "text": WHITE, "background": BLUE},
                     {"label": "Reject all", "kind": "button", "text": WHITE, "background": BLUE},
                     {"label": "Choose", "kind": "button", "text": BLUE, "background": WHITE}],
     "second_layer": {"analytics": False, "ads": False}, "withdraw_link": True},
]


def luminance(hex_colour):
    """Relative luminance of an sRGB colour, as WCAG 2.2 defines it."""
    channels = [int(hex_colour[i:i + 2], 16) / 255 for i in (1, 3, 5)]
    linear = [c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4 for c in channels]
    return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2]


def contrast(a, b):
    high, low = sorted((luminance(a), luminance(b)), reverse=True)
    return (high + 0.05) / (low + 0.05)


for banner in BANNERS:
    problems = []
    first = {item["label"].split()[0].lower(): item for item in banner["first_layer"]}
    accept, reject = first.get("accept"), first.get("reject")
    if not reject:
        problems.append("no reject option beside Accept on the first layer")
    else:
        ratio = contrast(reject["text"], reject["background"])
        if ratio < 4.5:
            problems.append(f"Reject's text contrast is {ratio:.2f}:1, under 4.5:1")
        if reject["kind"] != accept["kind"]:
            problems.append(f"Accept is a {accept['kind']}, Reject a {reject['kind']}")
    ticked = [purpose for purpose, on in banner["second_layer"].items() if on]
    if ticked:
        problems.append(f"pre-ticked: {', '.join(ticked)}")
    if not banner["withdraw_link"]:
        problems.append("no link on every page to change or withdraw the choice")
    print(f"Banner {banner['name']}: " + ("passes" if not problems else f"{len(problems)} problems"))
    for p in problems:
        print("   ", p)

Output

Banner 1, accept or go to settings: 2 problems
    no reject option beside Accept on the first layer
    no link on every page to change or withdraw the choice
Banner 2, reject in faint grey, boxes ticked: 3 problems
    Reject's text contrast is 1.30:1, under 4.5:1
    Accept is a button, Reject a link
    pre-ticked: analytics, ads
Banner 3, accept and reject alike: passes

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 banner_check.py

Banner 1 is the bakery’s first week: no Reject at all, and no way back. Banner 2 has a Reject whose text has a contrast of 1.30:1, as a link beside a button, with both purposes ticked in advance. Banner 3 passes: three buttons alike, nothing ticked, and a link on every page to change the choice. The bakery’s 412 acceptances in a week were the first design’s work, not the visitors’ choice.

Cookie Consent Banner Generator Generate a banner with Accept and Reject of equal weight, nothing pre-ticked, a link to change the choice and consent mode defaults set to denied.

Checkpoint

Check: A banner that makes refusing easy

  1. Question 1 of 2 The bakery's first banner offered Accept all and a small Settings link. What did the EDPB's taskforce say about such a design?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: For a vast majority of authorities, no reject option on any layer with an accept button does not give valid consent

    Put Reject on the first layer, beside Accept, with the same weight. The bakery's 412 acceptances and no refusals were the design's work.

  2. Question 2 of 2 Which designs make withdrawing consent as easy as giving it? Choose every one.

    Choose every answer that is right.

    Show the answer: Question 2 of 2

    Answer:

    • A Privacy choices page, linked from every page, with the same switches as the banner
    • A Cookie settings link on every page that reopens the same choices

    The GDPR says it shall be as easy to withdraw as to give consent, and the DPDP Act asks for comparable ease. Give the way back the same weight as the way in.

Education, not legal advice

This section explains the rules from their official texts so that you can talk to a lawyer about your own business. It does not decide what your business must do: that depends on where you operate, who your visitors are and what you collect.

The European rule is about the device itself. Storing information on a visitor’s device, or reading what is already there, is allowed only with the visitor’s consent, given after clear and comprehensive information, unless it is for “the sole purpose of carrying out the transmission” or strictly necessary for a service the visitor explicitly asked for (ePrivacy Directive, article 5(3)). It is not limited to cookies: the EDPB’s guidelines on its scope confirm that device fingerprinting falls within it (Guidelines 2/2023). The consent itself is the GDPR’s: “freely given, specific, informed and unambiguous”, by “a clear affirmative action” (article 4(11)), which the business must be able to demonstrate (article 7(1)) (GDPR). In the UK, regulation 6 of PECR sets the same kind of rule; the ICO says non-essential cookies need consent before they are set, consent must be more than “simply continuing to use the website”, and its detailed guidance is being revised (ICO).

The rules differ in what triggers them, and they agree on the shape of consent:

Rule EU and UK (ePrivacy or PECR, with the GDPR or UK GDPR) India (DPDP Act and Rules)
What needs consent Storing or reading information on a device, beyond the narrow exemptions Processing personal data, unless a legitimate use applies
Standard Freely given, specific, informed, unambiguous, a clear affirmative action Free, specific, informed, unconditional, unambiguous, a clear affirmative action
Withdrawal As easy as giving it Comparable in ease to giving it
Children EU: a parent’s consent under 16, or a lower age a member state sets, never under 13 (article 8); UK: set by its own data protection law Under 18: a parent’s verifiable consent, and no tracking or targeted ads

India’s Digital Personal Data Protection Act, 2023, is about personal data rather than the device, and its consent rule is strict. Consent must be free, specific, informed, unconditional and unambiguous, given by a clear affirmative action, for a specified purpose and only the data it needs (section 6(1)); the request for it comes with, or after, a notice that can be read in English or a language of the Constitution’s Eighth Schedule (section 5); withdrawing must be as easy as giving it (section 6(4)); and the business must be able to prove notice and consent (section 6(10)). A child is anyone under 18 (section 2(f)), whose data needs a parent’s verifiable consent; tracking children, monitoring their behaviour and advertising targeted at them are not allowed (section 9). A person may give and withdraw consent through a consent manager: Under India's DPDP Act, a person registered with the Data Protection Board who acts as a single point of contact through which people give, manage, review and withdraw their consent on one platform. registered with the Data Protection Board. The duties start in phases:

Timeline · 5 events in order When India's data protection duties apply Walk through the phases of the DPDP Act from the year it was passed to the year its core duties apply.
  1. 2023

    The Act is passed

    Parliament enacted the Digital Personal Data Protection Act in 2023. It comes into force on dates the Central Government notifies, and different provisions may get different dates.

  2. Rules notified

    The Rules and the dates 2025

    In 2025 the government notified the DPDP Rules and the dates of commencement, with an eighteen-month phased timeline. The definitions and the Data Protection Board's provisions applied at once.

  3. One year on

    Consent managers can register

    The registration of consent managers applies one year after the notification. A consent manager is registered with the Board and lets a person give, manage, review and withdraw consent from one platform; it must be an Indian company.

  4. Eighteen months on

    The core duties apply 2027

    Eighteen months after the notification, in 2027, the rest of the Act applies, notice, consent and its withdrawal, the duties of data fiduciaries and the rules for children's data among it, together with the matching Rules.

  5. From then on

    Notice, proof and requests

    A business that relies on consent gives a notice that stands on its own, with an itemised description of the data and the purpose, must be able to prove notice and consent, and answers requests to access, correct or erase data within 90 days at most.

The timeline has 5 events, from 2023 to From then on.

Penalties under the DPDP Act

The government’s backgrounder on the Rules lists penalties of up to ₹250 crore for failing to keep reasonable security safeguards, up to ₹200 crore for not notifying the Board or the people affected of a breach and for breaking the obligations about children, and up to ₹50 crore for other violations (Press Information Bureau).

Checkpoint

Check: ePrivacy, the GDPR and India's DPDP Act

  1. Question 1 of 2 A vendor offers to identify returning visitors from the browser's characteristics, without cookies, so that no banner is needed in the EU. Is it outside the ePrivacy rule?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: No; the EDPB confirms that device fingerprinting falls within article 5(3), like cookies

    Cookieless is not consentless. Device fingerprinting is within the technical scope of article 5(3), so it needs the same consent as a cookie.

  2. Question 2 of 2 Under India's DPDP Act, who counts as a child, and what may a business not do with children's data?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Anyone under 18; no tracking, behavioural monitoring or targeted advertising directed at children

    A child is anyone who has not completed 18 years. Their data needs a parent's verifiable consent, and tracking, behavioural monitoring and targeted advertising directed at children are not allowed.

Your own cookies need Secure, HttpOnly, SameSite and a short life

Consent decides whether a cookie may be set; how it is set is security. OWASP’s rules for a site’s own cookies are short (Session Management Cheat Sheet): Secure, so the cookie travels only over HTTPS; HttpOnly wherever the page’s scripts do not need the value; SameSite set explicitly to Strict or Lax, and None never without Secure; no Domain attribute, so the cookie stays with the host that set it; and the __Host- prefix for session cookies. Lifetimes are capped by the browser in any case: the cookie draft says the limit “SHOULD NOT be greater than 400 days” (RFC 6265bis), and Google notes that browsers can shorten even its own two-year _ga cookie, to at most 400 days in Chrome and 7 days in Safari (cookie usage). The program reads five Set-Cookie headers copied from the bakery’s server. Run it, then press Edit and add pb_visitor to SCRIPT_READS:

The bakery's own cookies against OWASP's checklist Python · cookies/cookie_attrs.py
"""Check the bakery's own Set-Cookie headers against the OWASP Session Management Cheat Sheet and RFC 6265bis.

OWASP: Secure on every cookie; HttpOnly where the page's scripts do not need the value; SameSite set explicitly, Lax or
Strict, and None only with Secure; no Domain attribute, so the cookie stays with the host that set it; the __Host- prefix
for session cookies. RFC 6265bis: browsers limit a cookie's lifetime, the limit "SHOULD NOT be greater than 400 days", and
a SameSite=None cookie without Secure is ignored. SCRIPT_READS lists the cookies the bakery's own scripts must read.
The headers are the bakery's (a fictional site), copied from its server's responses into set_cookie_headers.txt.
"""
SCRIPT_READS = {"pb_cart", "pb_consent"}
MAX_DAYS = 400


def parse(header):
    first, *attributes = [part.strip() for part in header.split(":", 1)[1].split(";")]
    name = first.split("=", 1)[0]
    attrs = {}
    for a in attributes:
        key, _, value = a.partition("=")
        attrs[key.lower()] = value
    return name, attrs


for line in open("set_cookie_headers.txt", encoding="utf-8").read().splitlines():
    name, attrs = parse(line)
    problems = []
    if "secure" not in attrs:
        problems.append("no Secure: it can travel over plain HTTP")
    if "httponly" not in attrs and name not in SCRIPT_READS:
        problems.append("no HttpOnly, though no script of the bakery reads it")
    same_site = attrs.get("samesite", "").lower()
    if not same_site:
        problems.append("no SameSite: set Lax or Strict explicitly")
    elif same_site == "none" and "secure" not in attrs:
        problems.append("SameSite=None without Secure: browsers ignore this cookie")
    if "domain" in attrs:
        problems.append(f"Domain={attrs['domain']} also sends it to every subdomain")
    if "max-age" in attrs:
        days = int(attrs["max-age"]) / 86400
        if days > MAX_DAYS:
            problems.append(f"asks for {days:.0f} days; browsers cap lifetimes, Chrome at {MAX_DAYS} days")
    lifetime = f"{int(attrs['max-age']) / 86400:.0f} days" if "max-age" in attrs else "until the browser session ends"
    print(f"{name} ({lifetime})" + (": ok" if not problems else ":"))
    for p in problems:
        print("   ", p)

Output

__Host-pb_session (until the browser session ends): ok
pb_cart (7 days): ok
pb_consent (180 days): ok
pb_visitor (1825 days):
    no Secure: it can travel over plain HTTP
    no HttpOnly, though no script of the bakery reads it
    no SameSite: set Lax or Strict explicitly
    Domain=punebakes.example also sends it to every subdomain
    asks for 1825 days; browsers cap lifetimes, Chrome at 400 days
pb_admin (until the browser session ends):
    no Secure: it can travel over plain HTTP
    SameSite=None without Secure: browsers ignore this cookie

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 cookie_attrs.py

set_cookie_headers.txt: the five headers the program reads Text · cookies/set_cookie_headers.txt
Set-Cookie: __Host-pb_session=3f8a1c; Path=/; Secure; HttpOnly; SameSite=Lax
Set-Cookie: pb_cart=eggless-1kg:1; Path=/; Secure; SameSite=Lax; Max-Age=604800
Set-Cookie: pb_consent=analytics:denied,ads:denied; Path=/; Secure; SameSite=Lax; Max-Age=15552000
Set-Cookie: pb_visitor=7d2e9b; Domain=punebakes.example; Path=/; Max-Age=157680000
Set-Cookie: pb_admin=91ac; Path=/admin; HttpOnly; SameSite=None

Three cookies pass: the session cookie with __Host-, Secure, HttpOnly and SameSite=Lax, and the cart and consent cookies, which the page’s scripts read and so go without HttpOnly. pb_visitor breaks five rules at once and asks for five years; pb_admin sets SameSite=None without Secure, which browsers ignore. A cookie nobody can explain, like pb_visitor, is usually best removed: it is one fewer identifier to justify under any of the laws above.

Security Headers Checker Check the Set-Cookie and other security headers that your own site really returns, next to the policy headers of the previous lesson.

Checkpoint

Check: Secure attributes for your own cookies

  1. Question 1 of 2 The bakery's pb_admin cookie is set with SameSite=None and without Secure. What do browsers do with it?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: They ignore it, because a SameSite=None cookie must also be Secure

    Set SameSite explicitly to Lax or Strict for your own cookies, and use None only with Secure where a cookie really must be sent cross-site.

  2. Question 2 of 2 pb_visitor asks for a lifetime of 1,825 days. What actually happens, and what is the better fix?
    Show the answer: Question 2 of 2

    Answer: Browsers cap it, Chrome at 400 days; better to give it the shortest workable life or remove it

    Ask for the shortest lifetime the cookie needs. Long lifetimes are capped anyway, and every extra identifier has to be justified under the rules of this page.

Measurement that survives a refusal is built on first-party data

When a visitor refuses, the honest answer is to measure less, not to measure around the refusal. Three things keep working. Orders, enquiries and bookings sit in the business’s own systems whatever the banner said, so the KPIs of the goals lesson can always be counted there, and GA4’s numbers become a sample to read trends from. Reports in totals, such as orders a week by channel from tagged links, need no identifier for each person. And where traffic is large enough, consent mode’s modelling fills some of the gaps.

The industry is not coming to the rescue with new identifiers. In 2025 Google announced that Chrome keeps users’ choice about third-party cookies and that most Privacy Sandbox APIs, the Attribution Reporting API, Protected Audience and Topics among them, are being retired “in light of their low levels of adoption”, while CHIPS, FedCM and Private State Tokens stay and an interoperable attribution standard is being worked on at the W3C (Privacy Sandbox update).

After a refusal Keeps working Stops, and should
Counting Orders and enquiries in your own systems A cookie or identifier for that visitor
Channels Totals from tagged links, in your own order records Following that visitor across visits
Estimates Consent mode modelling, above its thresholds Fingerprinting, which the EU’s rule covers too

Key idea

Ask before you store, make refusing as easy as agreeing, and measure what refusals leave you from your own records: a refusal is an answer to respect, not a gap to engineer around.

Privacy Policy Generator Draft the privacy notice your banner links to, listing what you collect, why, and how a visitor changes or withdraws a choice.

Checkpoint

Check: Measurement that survives a refusal

  1. Question 1 of 2 A visitor refuses all cookies and then orders a cake. Which of these can the bakery still count honestly? Choose every one.

    Choose every answer that is right.

    Show the answer: Question 1 of 2

    Answer:

    • The order, in its own order records
    • Orders a week by channel, from tagged links saved with each order

    First-party records and totals survive a refusal; identifiers engineered around it do not belong in any plan.

  2. Question 2 of 2 What did Google announce in 2025 about the Privacy Sandbox?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Most of its APIs, Attribution Reporting, Protected Audience and Topics among them, are being retired, while CHIPS, FedCM and Private State Tokens stay

    Do not plan measurement around identifiers that are going away. Plan it around your own records and consent.

Write a shop’s consent mode calls and banner settings: denied defaults for exactly the regions that see the banner, an update for each choice, and a banner that passes this page’s rules:

Cookie Policy Generator List the cookies your site sets, with their purpose and lifetime, for the policy your banner links to.

Then practise the page’s six ideas, from the consent calls to your own cookies, weakest first:

Practice that adapts to you

Practise this lesson

12 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 12 According to Google, who is responsible for obtaining a visitor's consent when a site uses consent mode?
    Show the answer: Question 1 of 12

    Answer: The site that runs the tags

    Consent mode is a messenger. Asking, recording and respecting the choice are the site's job.

  2. Question 2 of 12 Setup C sets only ad_storage and analytics_storage for EEA visitors. What does it miss?

    Choose one answer.

    Show the answer: Question 2 of 12

    Answer: ad_user_data and ad_personalization, the two types Google added to consent mode for EEA traffic

    Set ad_storage, ad_user_data, ad_personalization and analytics_storage in every default and update for EEA visitors.

  3. Question 3 of 12 What does advanced consent mode allow that basic mode does not?

    Choose one answer.

    Show the answer: Question 3 of 12

    Answer: Measurements without cookies before and after a refusal, which modelling can use

    Advanced mode changes what is sent while consent is denied: measurements without cookies instead of nothing.

  4. Question 4 of 12 A café's site has 300 visitors a day and wants advanced consent mode so that GA4 fills in the refusals. What do you tell it?

    Choose one answer.

    Show the answer: Question 4 of 12

    Answer: Behavioural modelling needs at least 1,000 consenting users a day, so it will not model these gaps; count orders in its own records instead

    Below the thresholds, modelling is not a plan. First-party records are.

  5. Question 5 of 12 The settings layer of a banner opens with the analytics and ads boxes already ticked. Is that valid consent?

    Choose one answer.

    Show the answer: Question 5 of 12

    Answer: No; pre-ticked boxes do not give valid consent

    Start every optional purpose unticked. Consent is something the visitor does, not something they fail to undo.

  6. Question 6 of 12 The program measured banner 2's Reject text against its background. What contrast ratio did it find, to two decimal places?

    Type a number.

    Show the answer: Question 6 of 12

    Answer: 1.3 :1 (anything from 1.295 to 1.305 counts)

    1.30:1, far under WCAG 2.2's 4.5:1 for normal text, and the kind of contrast the taskforce called manifestly misleading when it makes the alternative unreadable.

  7. Question 7 of 12 Which storage does article 5(3) of the ePrivacy Directive exempt from consent?

    Choose one answer.

    Show the answer: Question 7 of 12

    Answer: Storage for the sole purpose of carrying out a transmission, or strictly necessary for a service the user asked for

    Everything else stored on or read from a device needs consent with clear and comprehensive information.

  8. Question 8 of 12 When do the DPDP Act's core duties of notice and consent apply?

    Choose one answer.

    Show the answer: Question 8 of 12

    Answer: Eighteen months after the commencement notification of 2025, in 2027

    The Rules gave an eighteen-month phased timeline. A business has until 2027 to have notice, consent, withdrawal and proof working, and can start now.

  9. Question 9 of 12 When should a cookie of your own go without HttpOnly?

    Choose one answer.

    Show the answer: Question 9 of 12

    Answer: Only when the page's own scripts must read its value, like the bakery's cart and consent cookies

    HttpOnly is the default for your own cookies; drop it only for a cookie your scripts need, and know which ones those are.

  10. Question 10 of 12 What does the __Host- prefix require of a cookie?

    Choose one answer.

    Show the answer: Question 10 of 12

    Answer: Secure, Path=/ and no Domain attribute, so the cookie belongs to one host only

    A __Host- cookie cannot be set for other subdomains or paths, which protects session cookies from subdomain forgery.

  11. Question 11 of 12 Which number stays complete whatever visitors choose on the banner?

    Choose one answer.

    Show the answer: Question 11 of 12

    Answer: The orders in the bakery's own order system

    Count the KPIs in your own records, and read analytics as a sample that shows trends and paths.

  12. Question 12 of 12 After many refusals, a manager proposes fingerprinting the visitors who said no, to keep the numbers whole. How do you answer?

    Choose one answer.

    Show the answer: Question 12 of 12

    Answer: No; fingerprinting falls under the same EU rule as cookies and ignores the refusal, so measure from first-party records instead

    A refusal is an answer, not a gap to engineer around. Totals from your own records and tagged links carry the business decisions.

Interview questions

Warm-up (fresher to 2–5 years): basic or advanced consent mode, and how they behave when a visitor refuses

In basic consent mode, Google’s tags do not load until the visitor chooses; if they refuse, no data reaches Google, not even pings, and Google Ads conversion modelling falls back to a general model. In advanced consent mode, the tags load at once with denied defaults; they store no cookies and send measurements without cookies, such as the consent state, a timestamp, the user agent and the referrer, and after consent they store cookies and send everything. Advanced mode allows modelling specific to the advertiser, but only above thresholds: GA4’s behavioural modelling needs at least 1,000 events a day with analytics storage denied for 7 days and 1,000 consenting users a day on 7 of 28 days. Either way, the default must run before any measurement command, and obtaining valid consent stays the site’s job.

Two more questions follow, about cookies set before anyone clicks and about the DPDP Act. Choose your first move for each, then open it:

Checkpoint

Your first decision

  1. Question 1 of 2 Interview brief: on a shop's pages, visitors in Germany get a _ga cookie before they touch the banner. What do you check first?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Whether a consent default for their region runs before the Google tag's config on every page

    Look at the order and the scope of the default first: a default that runs late, or misses the visitor's region, lets the tag set its cookie before anyone chooses.

  2. Question 2 of 2 Interview brief: an Indian direct-to-consumer brand asks what the DPDP Act means for its tracking and its email list. Where do you start?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Map what personal data it collects, for which purposes and on what basis, against the Act's consent rule and its phased dates

    Start from the data and its purposes, then the consent rule and the dates.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    An Indian direct-to-consumer brand asks what the DPDP Act and Rules mean for its website tracking, its email list and its WhatsApp marketing, and by when it must be ready. How do you answer, as a marketer and not as its lawyer?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Consent mode passes each visitor’s choice to Google’s tags with a default that runs before any measurement command, scoped to the regions that see the banner, and an update for every change; obtaining consent stays the site’s job.
  • Basic mode loads no tags until the visitor chooses; advanced mode sends measurements without cookies, which feed modelling only above thresholds that small sites rarely reach.
  • A banner gives valid consent only when refusing is as easy as accepting: Reject on the first layer, alike Accept, nothing pre-ticked, and withdrawal as easy as consent.
  • The EU and UK rules cover storing and reading information on a device, fingerprinting included; India’s DPDP Act covers personal data, with strict consent, protection for everyone under 18, and duties that apply from 2027.
  • A site’s own cookies are Secure, HttpOnly where scripts do not read them, SameSite Lax or Strict, without Domain, and short-lived; browsers cap their lifetimes anyway.
  • Measurement that survives refusals comes from first-party records and totals, not from identifiers engineered around the refusal.

Terms in this lesson

consent manager
Under India's DPDP Act, a person registered with the Data Protection Board who acts as a single point of contact through which people give, manage, review and withdraw their consent on one platform.
consent mode
Google's way for a site to pass each visitor's consent choices to its tags: a default state set before any measurement, then an update when the visitor chooses. The site itself must still obtain the consent.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.