Back to Blog
    Developer Guide

    Cookie Consent Implementation: Technical Guide for Developers

    Build a cookie consent banner that blocks optional tags, stores granular choices, supports Google Consent Mode v2 and GPC, and can be tested before release.

    Cookie Consent Implementation: Technical Guide for Developers
    October 15, 2025
    10 min read
    cookie-consent
    developer-guide
    javascript
    cookie-banner
    google-consent-mode
    global-privacy-control
    gdpr
    ccpa
    Share:

    A cookie banner is only the visible part of a consent system. The implementation also needs to classify storage technologies, set a safe default before optional tags run, collect a clear choice, persist that choice, update vendors, support later withdrawal, and produce evidence that the flow works.

    This guide focuses on the queries developers actually use when solving the problem: cookie consent code, cookie acceptance code, cookie consent JavaScript, and script blocking. It uses a conservative default in which optional analytics and advertising are denied until the visitor makes a choice. That approach is useful when one implementation must support visitors from multiple regions, but your production rules should reflect the laws, users, and data practices that apply to your service.

    Start with an inventory. PolicyForge's cookie scanner can help identify browser storage and third-party requests, while the cookie consent banner guide covers configuration choices for a managed implementation.

    Do not treat every privacy law as the same rule

    For EU and UK visitors, rules governing cookies and similar storage technologies generally require clear information and consent before non-essential storage or access. Strictly necessary technologies can qualify for an exemption. Where consent is used under the GDPR or UK GDPR, it must also be freely given, specific, informed, unambiguous, and capable of being withdrawn.

    California takes a different approach. The CCPA does not create a blanket opt-in requirement for every cookie. Covered businesses must provide required notices, offer applicable rights to opt out of sale or sharing, and honor qualifying opt-out preference signals such as Global Privacy Control. Some processing, audiences, and data types can create additional requirements.

    The practical engineering lesson is to separate three questions:

    1. Is the storage or access strictly necessary for a service the visitor requested?
    2. Does the applicable region require prior consent for the optional technology?
    3. Does a sale, sharing, targeted advertising, or opt-out signal require a separate control?

    For background on the underlying regimes, use PolicyForge's GDPR guide and CCPA guide. Do not infer legal scope from an IP address alone without reviewing the limits and consequences of geolocation.

    Page starts
      -> set optional consent defaults to denied
      -> read the stored consent record
      -> read a supported browser privacy signal
      -> show the banner if a choice is still required
      -> save granular choices with a policy version and timestamp
      -> update consent-aware tags
      -> load optional scripts only for allowed categories
      -> expose a persistent Cookie Settings control
      -> retest whenever tags, purposes, vendors, or policy text change

    Keep the consent state independent from the banner UI. A state module should be the source of truth, while analytics, advertising, embeds, and experimentation tools subscribe to changes. This prevents each vendor integration from inventing its own interpretation of consent.

    Step 1: inventory and classify technologies

    Inspect cookies, local storage, pixels, SDKs, server-side tags, embedded media, and requests triggered through a tag manager. Record the owner, purpose, category, duration, data involved, destination, and the event that causes it to run. Confirm the result on a clean browser profile because an authenticated session can hide first-visit behavior.

    Use categories that match actual control boundaries. A common starting point is:

    • Necessary: authentication, security, load balancing, cart state, and functionality required for a service the visitor requested.
    • Preferences: optional interface choices that are not essential to the requested service.
    • Analytics: audience measurement and product analytics.
    • Marketing: advertising, cross-site measurement, retargeting, and audience creation.

    Classification depends on purpose and configuration, not the vendor name. The same SDK can behave differently when advertising features, user identifiers, or data sharing options are enabled.

    Step 2: set a denied default before Google tags

    If you use Google Consent Mode, define the default state before the Google tag or Tag Manager container runs. Consent Mode v2 includes ad_storage, analytics_storage, ad_user_data, and ad_personalization.

    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag() { dataLayer.push(arguments); }
    
      gtag('consent', 'default', {
        ad_storage: 'denied',
        analytics_storage: 'denied',
        ad_user_data: 'denied',
        ad_personalization: 'denied',
        wait_for_update: 500
      });
    </script>

    Google documents both basic and advanced Consent Mode. In basic mode, Google tags are blocked until the relevant consent is granted. In advanced mode, tags can load with denied consent and may send cookieless signals. Consent Mode is a way to communicate choices to Google products. It does not collect valid consent for you, replace the banner, or determine which legal rule applies.

    Step 3: add accessible banner and preference controls

    <section id="cookie-banner"
      role="dialog"
      aria-modal="true"
      aria-labelledby="cookie-title"
      hidden>
      <h2 id="cookie-title">Choose your cookie settings</h2>
      <p>
        We use necessary storage to operate this site. With your permission,
        we also use analytics and marketing technologies.
      </p>
      <p>
        <a href="/cookie-policy">Read the cookie policy</a>
      </p>
      <div class="cookie-actions">
        <button type="button" id="reject-all">Reject optional</button>
        <button type="button" id="manage-cookies">Manage choices</button>
        <button type="button" id="accept-all">Accept all</button>
      </div>
    </section>
    
    <dialog id="cookie-preferences" aria-labelledby="preferences-title">
      <form method="dialog">
        <h2 id="preferences-title">Cookie preferences</h2>
        <label>
          <input type="checkbox" checked disabled>
          Necessary
        </label>
        <label>
          <input type="checkbox" id="analytics-choice">
          Analytics
        </label>
        <label>
          <input type="checkbox" id="marketing-choice">
          Marketing
        </label>
        <button type="button" id="save-preferences">Save choices</button>
        <button type="submit">Cancel</button>
      </form>
    </dialog>
    
    <button type="button" id="open-cookie-settings">Cookie Settings</button>

    Give accept and reject choices comparable prominence. Do not preselect optional categories. The preference control must remain available after the banner closes so a visitor can withdraw or change consent.

    The following example stores one versioned record in localStorage, applies Google consent updates, respects GPC for the marketing category, and exposes a change event for other integrations.

    (() => {
      const STORAGE_KEY = 'pf_cookie_consent';
      const POLICY_VERSION = '2026-09-24';
    
      const banner = document.getElementById('cookie-banner');
      const dialog = document.getElementById('cookie-preferences');
      const analyticsChoice = document.getElementById('analytics-choice');
      const marketingChoice = document.getElementById('marketing-choice');
      const gpcEnabled = navigator.globalPrivacyControl === true;
    
      function readConsent() {
        try {
          const value = JSON.parse(localStorage.getItem(STORAGE_KEY));
          return value?.version === POLICY_VERSION ? value : null;
        } catch {
          return null;
        }
      }
    
      function persistConsent(choices) {
        const record = {
          necessary: true,
          analytics: Boolean(choices.analytics),
          marketing: gpcEnabled ? false : Boolean(choices.marketing),
          gpc: gpcEnabled,
          version: POLICY_VERSION,
          updatedAt: new Date().toISOString()
        };
    
        localStorage.setItem(STORAGE_KEY, JSON.stringify(record));
        return record;
      }
    
      function updateGoogleConsent(record) {
        if (typeof window.gtag !== 'function') return;
    
        window.gtag('consent', 'update', {
          analytics_storage: record.analytics ? 'granted' : 'denied',
          ad_storage: record.marketing ? 'granted' : 'denied',
          ad_user_data: record.marketing ? 'granted' : 'denied',
          ad_personalization: record.marketing ? 'granted' : 'denied'
        });
      }
    
      function applyConsent(record) {
        updateGoogleConsent(record);
    
        document.dispatchEvent(new CustomEvent('consentchange', {
          detail: record
        }));
    
        banner.hidden = true;
        if (dialog.open) dialog.close();
      }
    
      function save(choices) {
        applyConsent(persistConsent(choices));
      }
    
      function openPreferences() {
        const current = readConsent();
        analyticsChoice.checked = current?.analytics ?? false;
        marketingChoice.checked = gpcEnabled
          ? false
          : current?.marketing ?? false;
        marketingChoice.disabled = gpcEnabled;
        dialog.showModal();
      }
    
      document.getElementById('accept-all').addEventListener('click', () => {
        save({ analytics: true, marketing: true });
      });
    
      document.getElementById('reject-all').addEventListener('click', () => {
        save({ analytics: false, marketing: false });
      });
    
      document.getElementById('manage-cookies')
        .addEventListener('click', openPreferences);
    
      document.getElementById('open-cookie-settings')
        .addEventListener('click', openPreferences);
    
      document.getElementById('save-preferences')
        .addEventListener('click', () => {
          save({
            analytics: analyticsChoice.checked,
            marketing: marketingChoice.checked
          });
        });
    
      const stored = readConsent();
      if (stored) {
        applyConsent(stored);
      } else {
        banner.hidden = false;
      }
    })();

    Run this code after the banner elements exist, or wrap initialization in a DOMContentLoaded listener. Attach that listener to document, because a single banner element does not receive the document event.

    Step 5: connect vendors without creating race conditions

    Listen for the custom event and load each optional integration once:

    function loadScriptOnce(id, src) {
      if (document.getElementById(id)) return;
    
      const script = document.createElement('script');
      script.id = id;
      script.src = src;
      script.async = true;
      document.head.appendChild(script);
    }
    
    document.addEventListener('consentchange', (event) => {
      const consent = event.detail;
    
      if (consent.analytics) {
        loadScriptOnce(
          'ga4-script',
          'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX'
        );
        window.gtag?.('js', new Date());
        window.gtag?.('config', 'G-XXXXXXXXXX');
      }
    
      if (consent.marketing) {
        // Load the reviewed advertising integration here.
      }
    });

    Revocation needs vendor-specific handling. A script already loaded in memory cannot always be unloaded safely. On withdrawal, update the vendor consent API, prevent future events, remove cookies where supported, and consider a page reload after the new state is saved. Document the behavior in your test plan.

    Global Privacy Control

    A supported browser can expose GPC through navigator.globalPrivacyControl. For a business subject to the CCPA, a qualifying signal can represent a request to opt out of sale or sharing. It is not the same as accepting or rejecting every analytics purpose under every law.

    The sample disables the marketing category when GPC is enabled. A production system should also communicate the opt-out to server-side advertising flows and vendors, avoid asking the visitor to opt back in through a dark pattern, and record enough information to demonstrate that the signal was honored. PolicyForge's California privacy enforcement guide explains why GPC testing belongs in the release checklist.

    1. Start clean. Clear cookies, local storage, service workers, and cache. Open a private window.
    2. Inspect before interacting. In DevTools, confirm that optional cookies and vendor requests are absent when using basic blocking.
    3. Reject optional storage. Reload and confirm the saved denied state is applied before optional tags can run.
    4. Accept analytics only. Confirm analytics loads once and marketing remains blocked.
    5. Accept all. Confirm each reviewed integration loads once without duplicate page views.
    6. Withdraw consent. Use Cookie Settings, save a denied state, reload, and verify future requests are blocked.
    7. Test GPC. Enable a browser or extension that sends the signal and verify the marketing opt-out reaches both browser and server-side flows.
    8. Test accessibility. Use only the keyboard, check focus movement, confirm labels with a screen reader, and test at 200 percent zoom.
    9. Test failure modes. Block local storage, simulate a tag timeout, disable JavaScript, and confirm the site remains usable.

    Automate the highest-risk checks in Playwright, Cypress, or your existing end-to-end suite. Assert that named network requests do not occur before the required state and that changing preferences produces the expected vendor calls.

    Common implementation failures

    • Optional tags are embedded in the page before the consent code executes.
    • The banner changes appearance but does not change tag behavior.
    • Reject is less visible or requires more steps than accept.
    • A tag manager can deploy a new vendor outside the consent categories.
    • Choices are stored without a policy version, so material changes never trigger a new decision.
    • Browser consent is updated but server-side events continue unchanged.
    • GPC is detected but not propagated to advertising and data-sharing systems.
    • The cookie policy lists vendor defaults rather than the site's actual configuration.

    Keep the published disclosure aligned with the implementation. PolicyForge's cookie policy generator, cookie policy template, and cookie policy examples can help organize the document, but the final text must match the inventory and tests.

    Release checklist

    • Every storage technology and outbound tag has an owner, purpose, category, and retention value.
    • Optional defaults are set before dependent tags run.
    • Accept, reject, granular preferences, withdrawal, and GPC are tested.
    • The banner, privacy notice, cookie policy, and vendor configuration agree.
    • Consent records include the policy version, timestamp, and granular state.
    • Server-side collection follows the same state as the browser.
    • A deployment test fails if an unclassified tracking request appears.

    This implementation guide is technical information, not a determination that one configuration satisfies every law. Review higher-risk advertising, children's data, sensitive data, and cross-border processing with qualified counsel.

    Primary sources

    PET

    PolicyForge Editorial Team

    The PolicyForge Editorial Team publishes practical policy guidance using primary regulatory, legislative, and platform sources. Editorial review checks source support, dates, product accuracy, and useful next steps. It is not a substitute for advice from qualified counsel.

    Policy Workflows
    Primary Source Research
    AI Governance
    Product Documentation

    Ready to generate your legal policies?

    Create compliant privacy policies, terms of service, and more with AI assistance.