Skip to content

Implement Google Consent Mode v2 in Cookie Banner #4627

Description

@vfanucci

Context

We are launching Google Ads campaigns on kestra.io and need Google Consent Mode v2 properly implemented. Currently, GTM does not load at all until the user accepts cookies in Europe, which means Google cannot use conversion modeling to estimate conversions from users who decline cookies — causing ~10-30% underreporting of conversions in European markets.

Internal spec / business source of truth: Notion — Google Consent Mode v2 rollout.

Current Implementation (source: src/scripts/cookieconsent.ts)

Library: vanilla-cookieconsent (open-source)
Detection: Europe detection via Intl.DateTimeFormat().resolvedOptions().timeZone

Current behavior:

  • Non-Europe users: GTM + PostHog + marketing load immediately (no consent required) ✅
  • Europe users: GTM is not loaded at all until user clicks "Accept". The enabledAnalytics() function injects the GTM script tag only after consent is granted via the onConsent callback.

Two consent categories exist:

  • analytics → triggers enabledAnalytics() (loads GTM + PostHog)
  • marketing → triggers enabledMarketing() (pushes enable_marketing event to dataLayer)

The problem: Google Consent Mode v2 requires GTM to be loaded before consent, with all consent parameters set to denied. This allows Google to send anonymous pings for conversion modeling. Currently, GTM is not present on the page at all before consent, so Google receives zero signals.

Tag IDs

  • GTM container: GTM-T4F85WRF
  • GA4 property: G-EYVNS03HHR
  • Google Ads tag: AW-18075963910

Expected Behavior

The implementation should satisfy the following behavior. Implementation details are left to the developer — please refer to Google's Consent Mode v2 documentation for the canonical patterns.

Aspect Current Required
GTM loading (Europe) Only after consent Always loaded, with denied defaults
Consent signals to Google None denied by default → granted on accept
Conversion modeling Not possible Enabled (Google receives anonymous pings)

Specifically:

  1. Europe users, before any interaction with the banner: GTM is loaded, and the four Consent Mode v2 signals (ad_storage, ad_user_data, ad_personalization, analytics_storage) are set to denied before GTM loads, with wait_for_update configured.
  2. Europe users, after accepting a category: the relevant signals transition to granted via gtag('consent', 'update', ...).
  3. Europe users, after declining or only partially accepting: Google tags fire in cookieless ping mode for the still-denied signals (no client-side cookies, anonymous pings only).
  4. Europe users who later revoke consent via the Settings modal: the relevant signals must transition back to denied. This is the edge case the developer should explicitly handle and test.
  5. Non-Europe users: all four signals default to granted, behavior otherwise unchanged.

Out of scope — do not regress

  • PostHog initialization must continue to fire only after analytics consent (it is not a Google product and is not driven by Consent Mode v2 signals; just keep the existing trigger).
  • enable_marketing dataLayer event must continue to fire only after marketing consent.
  • No regression on existing analytics or marketing tracking for non-Europe users.

Acceptance Criteria

  • GTM Preview → Consent tab shows ad_storage, analytics_storage, ad_user_data, ad_personalization
  • Before banner interaction (Europe): all four parameters show denied, and GTM is loaded on the page
  • After clicking "Accept all" (Europe): parameters update to granted based on accepted categories
  • After declining (Europe): parameters remain denied, Google tags fire in cookieless mode
  • After revoking consent via Settings (Europe): parameters transition back to denied
  • Non-Europe users: all four parameters default to granted
  • PostHog still only initializes after analytics consent (unchanged)
  • enable_marketing dataLayer event still fires only after marketing consent (unchanged)

GTM Configuration Required (Marketing/SEO team — blocking)

The code change alone does not enable cookieless pinging. The following GTM configuration is required for the acceptance criteria to be met, and must be coordinated with this PR:

  1. Each Google Ads tag's "Consent Settings" must require ad_storage and ad_user_data.
  2. The GA4 tag's "Consent Settings" must require analytics_storage.

Priority

High — Prerequisite for accurate Google Ads conversion reporting and full GDPR compliance. Blocking conversion modeling for European paid campaigns.

References

Metadata

Metadata

Assignees

Labels

Type

Fields

No fields configured for Task.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions