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.
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
| Category | What It Covers | Default State |
|---|---|---|
| Necessary | Session handling, load balancing, security, CSRF tokens, basic language and currency preference. | Always on, cannot be disabled |
| Analytics | GA4, Hotjar or Clarity, or any usage-measurement tool. | Off until accepted |
| Advertisement | Google Ads, Meta Pixel, LinkedIn Insight Tag, DV360 or Floodlight, remarketing pixels. | Off until accepted |
| Functionality | Chat 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
