Original Research

The Cookie Consent Standard We Build to Under DPDP: A Complete Reference

This is the real, complete standard we hold every project to, not a summary of one. The non-negotiables, a reference banner pattern, working code, reusable copy, and a QA checklist a non-developer can run before any project ships.

Key Takeaways

  • A real project either meets every non-negotiable below, or it isn't launch-ready. Equal-prominence Accept/Reject, granular category toggles, an auditable consent log, no cookie walls, and an absolute prohibition on tracking anyone under 18, regardless of consent.
  • The reference pattern is a real, two-layer structure: an initial banner with three equal actions (Accept All, Decline All, Manage Cookies), and a preference centre with independently toggleable categories, reopenable anytime from a persistent footer link.
  • The build guide includes real, working code: setting Consent Mode v2's default-denied state, persisting a returning visitor's choice, gating non-Google tools like Meta Pixel that have no native consent-mode support, and writing an actual audit-trail log, not just a client-side flag.
  • A QA checklist any non-developer can run: open DevTools, watch the Network tab, and confirm exactly what should and shouldn't fire before and after each choice, no coding knowledge required.

The Non-Negotiables

Any project, new build or existing site, should satisfy every item below before it's considered launch-ready from a consent standpoint. This isn't a best-practice wish list; each of these maps directly to a real requirement under India's DPDP Act, 2023 and the DPDP Rules, 2025.

Looking specifically for Google Consent Mode v2 setup and what it means for remarketing? This piece covers the complete consent standard across every tool. For a focused, step-by-step Consent Mode v2 walkthrough, including whether it can genuinely be implemented in India and what actually happens to Meta and Google remarketing, read that guide directly.

No tag fires before a choice is made
No non-essential cookie, pixel, or tracking script fires before the user makes a choice. This includes analytics (GA4), advertising (Google Ads, Meta Pixel, Floodlight/DV360, LinkedIn Insight), and any embedded third-party widget that sets cookies (YouTube, chat widgets, maps).
Equal prominence, no dark patterns
The consent banner offers Accept All and Reject/Decline All with equal visual prominence, no pre-checked boxes, no hiding Reject behind extra clicks or smaller, greyed-out styling.
A real, granular preference centre
A “Manage Cookies” preference centre is available, showing at minimum: Necessary (always on), Analytics, Advertisement, and Functionality, each independently toggleable.
A real, retrievable audit trail
The user's choice is stored with a timestamp and a version identifier for the notice text they agreed to, and is retrievable later. Not just a client-side flag, a real record that can be produced as evidence.
Changeable at any time
The choice can be changed at any time via a persistent, easy-to-find control, typically a footer link reading “Cookie Settings” or “Manage Cookies.”
A real Cookie Policy
A linked Cookie Policy explains, in plain language, what's collected, which third parties receive it, retention periods, and how to withdraw consent.
Rejecting is as easy as accepting
One click for either, not one click to accept and several to reject.
No cookie walls
Access to the site or its content is never made conditional on accepting non-essential cookies. Reject must lead to a fully usable site, not a blocked or degraded one used to pressure Accept.
An absolute prohibition on tracking minors
No behavioural tracking, profiling, or targeted advertising directed at anyone the site knows or has reasonable grounds to believe is under 18, and this cannot be switched on by consent, parental or otherwise.
Copy that actually says something
Banner and preference-centre copy itemises what's collected and why in plain language, not a generic “we use cookies to improve your experience” line with no real substance behind it.
A real path to withdraw consent or raise a grievance
A direct link or contact for withdrawing consent, exercising data-principal rights, and raising a grievance, not buried inside a general “Contact Us” page.
Consent isn't indefinite
Preferences are re-confirmed periodically, recommended every 12 months, or immediately if the categories or vendors in use change, rather than relied on forever from a single click.

The Reference Pattern: What “Good” Actually Looks Like

Use this two-layer pattern as the default reference implementation unless a specific design system dictates otherwise. It's a widely adopted, low-risk pattern that satisfies every non-negotiable above.

Layer 1, the initial banner: shown on first visit, with three actions at equal prominence, Accept All, Decline All, and Manage Cookies (which opens Layer 2). Plain-language copy states that a default applies if the user doesn't choose, and links to the full Cookie Policy.

Layer 2, the preference centre: opened from “Manage Cookies” on the banner, or from a persistent footer link at any later time. Necessary is locked on; every other category is an independent toggle. Accept All and Decline All remain available here too, alongside a Save Settings action for a custom mix.

Category Definitions

CategoryWhat It CoversDefault State
NecessarySession handling, load balancing, security, CSRF tokens, basic language and currency preference.Always on, cannot be disabled
AnalyticsGA4, Hotjar or Clarity, or any usage-measurement tool.Off until accepted
AdvertisementGoogle Ads, Meta Pixel, LinkedIn Insight Tag, DV360 or Floodlight, remarketing pixels.Off until accepted
FunctionalityChat widgets, video embeds (YouTube, Vimeo), map embeds, and other third-party widgets that set their own cookies.Off until accepted

The Step-by-Step Build Guide

Whether building fresh or retrofitting an existing site, these seven steps cover the full implementation.

1

Inventory every tag on the site

List every script that sets a cookie or sends data to a third party: the GTM container, GA4, Google Ads, Meta Pixel, LinkedIn, Floodlight/DV360, chat widgets, video and map embeds, any SEO tracking tools. Assign each to a category from the table above before touching any code.

Screenshot to be added
2

Decide the build approach

For most projects, a Consent Management Platform (Cookiebot, CookieYes, Osano, OneTrust) integrated via GTM is faster to deploy correctly and produces the granular UI and consent logging out of the box. A custom build, the reference pattern above, is acceptable when a CMP license isn't in budget or full design control matters, but it requires real, disciplined QA using the steps that follow.

Screenshot to be added
3

Set the default consent state before any tag fires

Whichever approach is used, the container or consent logic must set every non-necessary category to “denied” by default, before GTM or any inline tracking script executes. For the Google stack specifically, Google Consent Mode v2 should be the very first script in the page's head, before the GTM snippet itself.

Screenshot to be added
4

Gate everything else

Route GA4, Ads, and Floodlight through GTM tags with Consent Settings configured on each one. For tools with no native consent-mode support, Meta Pixel, LinkedIn Insight, chat and video widgets, do not initialise them at all until the relevant category is accepted; there's no partial state to fall back on for these.

Screenshot to be added
5

Wire the UI to consent logic, and log it

Accept All, Decline All, and Save Settings each need to: update the consent-mode state for Google tags, load or skip non-consent-mode-aware scripts accordingly, persist the choice client-side so repeat visits don't re-prompt unnecessarily, and send a real record (categories, timestamp, notice version, session reference) to a backend log. Client-side storage alone isn't sufficient, it doesn't survive a cache clear and can't be produced as evidence later.

Screenshot to be added
6

Ship the Cookie Policy and a persistent footer link

The Cookie Policy should itemise the categories, name the third parties in each, state retention periods, and give a contact for consent-related queries. Add a persistent “Cookie Settings” link in the footer that reopens the preference centre at any time.

Screenshot to be added
7

QA before handover, every time

Fresh incognito session, confirm zero analytics or ad requests fire before any click. Decline All, confirm nothing fires on subsequent navigation. Accept All, confirm everything now loads. Toggle one category on and another off, confirm only the accepted category's requests fire. Reload after each scenario, confirm the choice persists without re-prompting. Confirm a real record appears in the consent log for each interaction.

Screenshot to be added

Setting the Default Consent State

Place this immediately above the GTM snippet in the page's , so it's the very first thing that runs.

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  // Default: everything denied until the user chooses
  gtag('consent', 'default', {
    'ad_storage': 'denied',
    'ad_user_data': 'denied',
    'ad_personalization': 'denied',
    'analytics_storage': 'denied',
    'wait_for_update': 500
  });
</script>
<!-- GTM snippet goes here, unmodified -->

Wiring Accept, Decline, and Save Settings

GA4, Google Ads, and Floodlight tags configured in GTM with Consent Settings on each tag respect this state automatically. Non-consent-mode-aware tools (Meta Pixel, LinkedIn Insight, chat or video widgets) must be held back manually, shown next.

document.getElementById('acceptBtn').addEventListener('click', function () {
  var consent = { necessary: true, analytics: true, marketing: true,
                  ts: Date.now(), version: 'v1' };
  localStorage.setItem('cookie_consent', JSON.stringify(consent));
  gtag('consent', 'update', {
    'ad_storage': 'granted', 'ad_user_data': 'granted',
    'ad_personalization': 'granted', 'analytics_storage': 'granted'
  });
  if (typeof fbq === 'undefined') { loadMetaPixel(); }
  logConsent(consent);
  hideConsentPopup();
});

document.getElementById('rejectBtn').addEventListener('click', function () {
  var consent = { necessary: true, analytics: false, marketing: false,
                  ts: Date.now(), version: 'v1' };
  localStorage.setItem('cookie_consent', JSON.stringify(consent));
  gtag('consent', 'update', {
    'ad_storage': 'denied', 'ad_user_data': 'denied',
    'ad_personalization': 'denied', 'analytics_storage': 'denied'
  });
  logConsent(consent);
  hideConsentPopup();
});

Holding Back Meta Pixel Until Consent Is Granted

Meta Pixel has no native consent-mode support, so it must be held back entirely, not just muted.

function loadMetaPixel() {
  !function(f,b,e,v,n,t,s){/* ...standard fbq bootstrap, unchanged... */}
  (window, document,'script','https://connect.facebook.net/en_US/fbevents.js');
  fbq('init', 'YOUR_PIXEL_ID');
  fbq('track', 'PageView');
}

// On page load, auto-load only if a prior visit already granted marketing consent
(function () {
  var stored = localStorage.getItem('cookie_consent');
  if (stored && JSON.parse(stored).marketing) { loadMetaPixel(); }
})();

A Real, Minimal Consent Log

This is what turns “we have a banner” into “we can demonstrate consent was obtained.” A simple append-only log table (session reference, categories, timestamp, notice version, user agent) is enough on the backend.

function logConsent(consent) {
  fetch('/api/consent-log', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({
      sessionOrUserRef: /* existing session identifier */,
      categories: consent,
      timestamp: consent.ts,
      noticeVersion: consent.version,
      userAgent: navigator.userAgent
    })
  });
}

Consent Auto-Expiry

Supports the requirement that consent isn't treated as indefinite. Run this on every page load, before re-applying any stored consent.

(function () {
  var EXPIRY_DAYS = 365; // align with your own re-consent policy
  var stored = localStorage.getItem('cookie_consent');
  if (stored) {
    var consent = JSON.parse(stored);
    var ageDays = (Date.now() - consent.ts) / (1000 * 60 * 60 * 24);
    if (ageDays > EXPIRY_DAYS) {
      localStorage.removeItem('cookie_consent');
      // Denied defaults apply again; banner logic shows the prompt
      // as if this were a first visit.
    }
  }
})();

Reusable Reference Copy

The wording below is genuinely reusable, organisation-agnostic reference copy. Replace the organisation name and Cookie Policy link with your own, and translate it if your audience meaningfully includes non-English speakers, since real, informed consent depends on language a visitor actually understands.

Banner copy: “We use cookies to improve your experience on [Organisation Name]'s website. Some cookies are necessary for the site to function properly. Others are optional and help us understand how visitors use our site, remember your preferences, and show relevant content and advertising. You can accept all cookies, decline all optional cookies, or manage your preferences. If you don't make a selection, our default settings will apply. You can change your choice at any time. Learn more in our [Cookie Policy].”

A real, important caveat: this copy assumes no advertising cookies are set for anyone the site knows or reasonably believes to be a minor. For any site likely to be accessed by children, replace the Advertisement category copy and default behaviour entirely, on your own legal team's guidance, don't use this boilerplate as-is in that case.

A QA Checklist Anyone Can Run, No Coding Required

Use this before any launch, and periodically on live sites. Open the site in an incognito or private window, open the browser's DevTools (F12) and go to the Network tab, before doing anything else.

Before clicking anything
In the Network tab, search for “google-analytics,” “doubleclick,” “googleadservices,” “facebook.net,” and any other third-party tool the site uses. None of these should appear.
Click Reject, then reload
Still no tracking requests should appear.
Click Accept on a fresh incognito session
Tracking requests for GA4, Ads, Pixel, and any other accepted tools should now appear.
Open the preference centre
It should show independently toggleable categories, not one master switch.
Look for a persistent “Cookie Settings” link
Usually in the footer, reopening preferences at any later time.
Check the Cookie Policy link
It should work, and should name the third-party tools actually in use on the site.
Ask to see the consent log entry for your own test click
It should show a real timestamp and the categories you selected, not just a client-side flag.

Why does all of this matter legally? This piece is the build reference; the full legal breakdown, why cookies fall under DPDP even though the Act never names them, the real enforcement timeline, penalty figures, the BRDCMS signal, and children's-data rules, lives in a dedicated article. Read the full DPDP legal and regulatory breakdown.

This is general guidance, not legal advice. Exact figures, retention periods, and applicability (for example, whether an organisation qualifies as a Significant Data Fiduciary) change as the Rules are progressively notified, so any real implementation should still get sign-off from qualified legal or compliance counsel before launch.

References

  • Digital Personal Data Protection Act, 2023, and the DPDP Rules, 2025 (notified by MeitY, 13 November 2025).
  • Google, Consent Mode: Frequently Asked Questions, and Consent Mode v2 technical documentation.

Need help implementing this standard on your own site? Reach out to us for a no-obligation chat, or explore our Brand & Digital Strategy practice.

Let's talk about what this could look like for your brand

Start a Project