Skip to content

Consent Mode Setup in GTM

Consent Mode configuration in GTM is split between two places: code that runs before GTM loads (setting the default state), and GTM itself (handling the consent update and configuring per-tag consent requirements). Getting the sequencing wrong is the most common implementation mistake.

This article walks through the complete setup in the correct order.

Section titled “Step 1: Set default consent state before GTM”

The default consent state must be set before the GTM snippet. This is non-negotiable. If you set it inside GTM, there is a timing gap where tags may fire without a consent state defined.

Add this code block immediately before your GTM snippet in <head>:

<!-- Consent Mode default — MUST come before GTM snippet -->
<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',
'functionality_storage': 'denied',
'personalization_storage': 'denied',
'security_storage': 'granted',
'wait_for_update': 500
});
</script>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXX');</script>
<!-- End Google Tag Manager -->

If you operate in both EU and non-EU markets, use regional overrides:

// Default: granted globally
gtag('consent', 'default', {
'ad_storage': 'granted',
'analytics_storage': 'granted',
'ad_user_data': 'granted',
'ad_personalization': 'granted',
'security_storage': 'granted'
});
// Override: denied for EEA + UK
gtag('consent', 'default', {
'ad_storage': 'denied',
'analytics_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'wait_for_update': 500,
'region': ['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','NO','IS','LI','GB']
});
Section titled “Step 2: Configure the Consent Initialization trigger”

GTM has three initialization trigger groups that fire in order:

  1. Consent Initialization — All Pages: fires first, before any tags
  2. Initialization — All Pages: fires second
  3. All Pages (Page View): fires third, your normal tags

Your consent default tag (if managed through GTM rather than inline script) must fire on Consent Initialization. Everything else fires after. This trigger is in the Trigger configuration dropdown under “Initialization.”

When your CMP fires a consent update, you need to propagate that update to Google’s Consent Mode system. The mechanism depends on your CMP.

Option A: CMP fires a custom dataLayer event

Section titled “Option A: CMP fires a custom dataLayer event”

Many CMPs push a custom event when consent is granted. Create a Custom Event trigger listening for that event, and a tag that calls the consent update:

Trigger: Custom Event, Event Name = consentUpdate (or whatever your CMP pushes)

Tag (Custom HTML):

<script>
gtag('consent', 'update', {
'ad_storage': {{DLV - consent.advertising}} === 'true' ? 'granted' : 'denied',
'analytics_storage': {{DLV - consent.analytics}} === 'true' ? 'granted' : 'denied',
'ad_user_data': {{DLV - consent.advertising}} === 'true' ? 'granted' : 'denied',
'ad_personalization': {{DLV - consent.advertising}} === 'true' ? 'granted' : 'denied'
});
</script>

Where {{DLV - consent.advertising}} is a Data Layer Variable reading consent.advertising from the dataLayer push your CMP makes.

Most major CMPs provide a Community Template Gallery integration that handles the consent update automatically. See CMP Integration for CMP-specific setup instructions.

Option C: CMP exposes a JavaScript callback

Section titled “Option C: CMP exposes a JavaScript callback”

If your CMP has a JavaScript API, call the consent update directly from the CMP callback:

// Example: CookieYes callback pattern
document.addEventListener('cookieyes_consent_update', function(evt) {
var accepted = evt.detail.accepted || [];
gtag('consent', 'update', {
'analytics_storage': accepted.includes('analytics') ? 'granted' : 'denied',
'ad_storage': accepted.includes('advertisement') ? 'granted' : 'denied',
'ad_user_data': accepted.includes('advertisement') ? 'granted' : 'denied',
'ad_personalization': accepted.includes('advertisement') ? 'granted' : 'denied'
});
});
Section titled “Step 4: Configure consent settings on individual tags”

Every tag in GTM can have consent requirements configured under Advanced Settings → Consent Settings. This is a per-tag safeguard that prevents a tag from firing even if the consent update mechanism fails.

For each tag, configure:

  • Additional consent checks required: select the consent type(s) this tag requires
  • GTM will not fire the tag unless all required consent types are granted

Standard mappings:

  • GA4 Configuration and Event tags: analytics_storage
  • Google Ads Conversion Tracking: ad_storage + ad_user_data
  • Google Ads Remarketing: ad_storage + ad_personalization
  • Meta Pixel, LinkedIn, TikTok: use the blocking approach in Consent for Non-Google Tags

Returning visitors have already made a consent choice stored in your CMP’s cookie. Your CMP should read this cookie and fire the consent update immediately — before the wait_for_update timeout expires.

Verify this works correctly:

  1. Grant consent on a test browser
  2. Close and reopen the browser
  3. Visit the site again
  4. Check that the consent update fires before GTM’s first tag evaluation

If returning visitors trigger the wait_for_update timeout instead of restoring previous consent immediately, your CMP is not reading its stored consent on page load quickly enough. Check your CMP’s consent persistence configuration.

GTM’s Preview mode has a dedicated Consent tab that shows the current consent state at any point in the page lifecycle.

  1. Open GTM Preview mode and load your site
  2. Click on any event in the timeline (e.g., gtm.js)
  3. Select the Consent tab in the event detail panel
  4. You will see each consent type and its current state (granted, denied, or undefined)

What to check:

  • On first load (before CMP interaction): all types should be denied for EU users
  • After accepting all consent: all types should be granted
  • After rejecting all consent: advertising types should be denied, potentially analytics too

If you see undefined for any consent type, your default state is not being set correctly.

Understanding exactly what wait_for_update does is important for diagnosing consent timing issues.

When GTM processes a tag that requires consent, it checks the current consent state. If the state for a required type is denied:

  1. GTM checks whether wait_for_update is still active
  2. If active: GTM queues the tag and waits
  3. When the consent update arrives (or the timeout expires): GTM re-evaluates queued tags
  4. If consent is now granted: queued tags fire
  5. If the timeout expires with consent still denied: tags do not fire (correct behavior)

The timeout clock starts when the gtag('consent', 'default') call is processed. It is a one-time window per page load, not a rolling timer.

What happens when the timeout expires:

  • Tags that require denied consent types are silently dropped
  • No error is thrown
  • Preview mode shows the tag as “not fired” — check the Consent tab to understand why
// Verify consent state programmatically (useful for debugging)
// Run in browser console after page load
console.log(
window.google_tag_data?.ics?.entries
);

This outputs the current internal consent state object. Each key is a consent type, each value includes the current state and how it was set.

Forgetting security_storage: 'granted' in the default. Security storage should be granted unconditionally. If you deny it by default and never update it, some Google security features may not function.

Setting consent defaults via the Google Tag in GTM. The Google Tag has a “Set consent defaults” option in its configuration. This fires after GTM loads — too late. Use the inline script approach.

Not testing the consent denial flow. Always test with a fresh private/incognito browser session where no consent cookie exists. The first-visit experience is the most legally sensitive.

Misreading the Preview mode Consent tab. The tab shows the state at the time of the selected event. Click on the consent update event (e.g., consentUpdate) to see the state after it fires, not before.