# 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.

> Canonical: https://www.flowconsent.com/en/doc/google-consent-mode
> Last updated: 2026-07-30
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**:

```html title="Consent Mode stub (first script in the <head>)"
<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.

> [!IMPORTANT]
> The opposite case is the one to fear: a hit telling Google consent was **granted** despite an actual refusal. The scan flags those as aggravated violations — they usually mean a tag escaped the banner entirely.

## 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.

> [!TIP]
> The installation assistant (Builder → Integration) verifies the whole chain on your live page: stub present, stub before every Google tag, loader with the right license.
