Documentation menu

Google Consent Mode v2 with FlowConsent

How FlowConsent implements Consent Mode v2: denied defaults before any tag, wait_for_update, cookieless gcs=G100 pings, and lost-race detection.

View as Markdown
Last updated

FlowConsent implements Google Consent Mode v2 out of the box: an inline stub records denied defaults for all four consent signals before any Google tag loads, the banner sends gtag('consent', 'update', …) when the visitor chooses, and Google's cookieless pings (gcs=G100) keep conversion modeling alive without violating a refusal. No extra template, no third-party plugin.

The stub: denied by default, before every tag

Your integration snippet starts with this inline script — it is generated for you in Builder → Deployment → Integration:

Consent Mode stub (first script in the <head>)
html
<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
});
gtag('set','url_passthrough',true);
gtag('set','ads_data_redaction',true);
</script>

All four v2 signals — ad_storage, analytics_storage, ad_user_data, ad_personalization — start denied. The banner bundle registers the same defaults, but it loads asynchronously: a Google tag added by another channel (site-builder settings, a plugin) could beat it. The stub executes at HTML parse time, so the defaults are in place before any tag, whoever loads it. That is why the stub must stay before the loader in your snippet.

wait_for_update

wait_for_update: 500 tells Google tags to hold their first hits for up to 500 ms, giving the banner time to push the visitor's stored choice from a previous visit. A returning visitor who already accepted gets full measurement without seeing hits fire as denied first. The delay is configurable in your banner's Consent Mode settings.

url_passthrough and ads_data_redaction

Both are enabled by default and can be turned off in the banner configuration:

  • url_passthrough — passes ad-click information (gclid, dclid…) through URL parameters while consent is denied, so a later consent does not lose the attribution.
  • ads_data_redaction — when ad_storage is denied, Google further redacts ad-click identifiers from its cookieless pings.

What a refusal looks like: gcs=G100

When the visitor refuses, tags stay in cookieless mode: no cookies are written and Google receives anonymous pings carrying gcs=G100 ("consent denied" signal). These pings are compliant with the refusal — FlowConsent's compliance scan recognizes them, reports them separately, and does not count them as violations. A site using Consent Mode correctly is never penalized for them.

The race FlowConsent cannot win — but detects

Consent Mode defaults only protect tags that enter the dataLayer after them. If a gtag('config', 'G-XXXX') is queued before the consent defaults — typically a site builder's native analytics integration injected above your custom code, as Webflow's settings do — Google processes it unrestricted and sets its cookies no matter what any CMP does afterwards.

The banner checks for exactly this at startup and again after page load: if a config precedes the consent default in the dataLayer, it logs a console error naming the tag IDs and reports a ConsentDefaultRaceLost event to FlowConsent telemetry. The fix is always the same: remove the tag from the channel that injects it early, and declare the service in Builder → Services so the banner manages it.