← All posts

GA4 cookies before consent: the Consent Mode default arrives too late

If GA4 writes cookies before the user answers the banner, the usual culprit is load order: the Consent Mode default state is declared too late.

Step 1 of 5 This is how the page loads today: Google Tag Manager starts and the GA4 tag sends the first page_view.
  1. This is how the page loads today: Google Tag Manager starts and the GA4 tag sends the first page_view.
  2. The cookie banner is still unanswered, but GA4 has already written its _ga cookies.
  3. The cause: the Consent Mode default state is declared after the first tag.
  4. The fix: declare the default (denied) before any tag, in Consent Initialization.
  5. To check it, look at the gcs parameter on each hit: G100 before Accept and G111 after.

Consent Mode is how Google’s tags adapt their behaviour to the user’s cookie choice. To work, it needs a state before any tag runs: the default state. When that default arrives late, GA4 behaves as if consent had been granted during the first milliseconds of every page, and those milliseconds are enough to write cookies and send data that should not exist.

The page starts loading, Google Tag Manager starts and the Google tag sends the first page_view. A few hundred milliseconds later, the consent default command runs with analytics_storage: 'denied'. By then, the browser already holds the _ga and _ga_<ID> cookies, even though the cookie banner is still on screen, unanswered.

The problem is twofold. It is a compliance issue: in most European jurisdictions, analytics cookies can’t be stored without prior consent. And it is a data quality issue: hits sent without a consent state are not treated like hits that have one, which distorts modelling and consent reporting.

Why GA4 sets cookies before the banner is answered

The default state is declared at a point of the load that isn’t the first one. The most common cases:

  • The consent management platform (CMP) loads asynchronously and declares the default once it has finished initialising.
  • In GTM, the tag that declares the default uses the All Pages trigger instead of Consent Initialization – All Pages, so it races the other tags.
  • The gtag('consent', 'default', …) snippet is in the <head>, but after the GTM or gtag.js snippet.
  1. Open the site in a private window and, without touching the banner, check Application → Cookies in the developer tools. If _ga or _ga_<ID> are there, there is a problem.
  2. Use Tag Assistant and check the event order: the first event in the list must be Consent with the default state, before any Container Loaded or tag.
  3. In the Network tab, filter by collect and look at the gcs parameter of the first hit. If it is missing, the hit left without a consent state.
  • In GTM, move the default-state tag (or your CMP template) to the Consent Initialization – All Pages trigger, which runs before anything else.
  • If you declare the default with gtag, place it in the <head> before the GTM or gtag.js snippet.
  • If the CMP needs time to read a stored choice, use wait_for_update in the default command so tags wait a few milliseconds for the consent update.
gtag('consent', 'default', {
  ad_storage: 'denied',
  ad_user_data: 'denied',
  ad_personalization: 'denied',
  analytics_storage: 'denied',
  wait_for_update: 500,
});

Google’s documentation: Set up consent mode on websites.

The gcs parameter on each request to GA4 sums up the state: it reads G1 followed by two digits, the first for ad_storage and the second for analytics_storage (1 granted, 0 denied).

  • Before Accept, there must be no GA4 cookie. With advanced mode, any hits that go out must carry gcs=G100; with basic mode, no hit should go out at all.
  • After Accept, the next hit must carry gcs=G111 and the cookies must be created at that moment.

Repeat the test rejecting the banner and with a stored choice, which is where a race between the CMP and the tags shows up most often.