Skip to content
$_ setuptracking
Cookieless

Consent Mode v2 Without Google: Self-Hosted Analytics Patterns That Pass CNIL (2026)

12 min read
Cookie consent banner with Accept and Reject buttons in flat orange illustration style

Google Consent Mode v2 became mandatory for any site sending data to Google Ads or Google Analytics from the EU on March 6, 2024. If you’ve already moved your analytics off Google — Plausible, Matomo, Umami, PostHog — you’re sitting in a strange place. The signal flow Google designed doesn’t technically apply to you. But if you still run any Google product (Ads, Tag Manager firing third-party pixels, reCAPTCHA), you’re back on the hook.

This guide is for that hybrid stack: self-hosted analytics + leftover Google touchpoints. We walk through what Consent Mode v2 actually transmits, why a pure self-hosted stack can ignore it, and three production patterns for when you can’t.

What Consent Mode v2 actually does (and what it doesn’t)

Most explainers conflate three different things: the JavaScript API, the consent signal sent with each hit, and the “advanced mode” behavioural modeling on Google’s side. Let’s separate them.

Consent Mode is a JavaScript API that exposes two new parameters to Google’s tags — ad_user_data and ad_personalization — added to v2 in late 2023 (alongside the existing ad_storage and analytics_storage from v1). When a user lands on your page, your Consent Management Platform (CMP) calls gtag('consent', 'default', {...}) with one of two states per parameter: 'granted' or 'denied'. The user then makes a choice in your banner, and the CMP calls gtag('consent', 'update', {...}) with the new state.

Google’s tags read those flags and adjust behaviour:

  • Basic mode: when consent is denied, tags don’t fire. No hit goes out. You lose all visibility into denied-consent users.
  • Advanced mode: when consent is denied, tags still fire but send cookieless pings with the consent state attached. Google then runs behavioural modeling to estimate what the missing data would have looked like, based on the granted-consent users you do have.

That second mode is the one EU regulators have been arguing about. The CNIL has not banned it outright but has explicitly warned that modeled data must not be used to identify individuals, and the EDPB’s 2024 opinion on cross-border transfers is even less forgiving. (See the CNIL’s original Google Analytics decision from February 2022 — the chain of reasoning still applies to v2.)

Why a pure self-hosted stack doesn’t need Consent Mode

Consent Mode is a Google-product feature. It exists because Google’s tags need to know whether they’re allowed to read/write cookies and ad identifiers. If your analytics stack is Plausible, Matomo (cookieless mode), Umami, or PostHog with anonymous capture — none of those tools read or write cookies in their default self-hosted configuration. They also don’t send ad identifiers anywhere. There is no “denied” state for them to honour because they were never going to do the thing being denied.

For Plausible specifically: no cookies, no localStorage, IP is hashed with a daily-rotating SipHash key and discarded, no fingerprinting. The same applies to Umami at default settings and Fathom. Matomo gets there with three settings: enable Anonymise data (Privacy → Anonymise data), enable Cookieless tracking (Privacy → Don’t use cookies), and disable UserID. PostHog requires persistence: 'memory' and opt_out_capturing_by_default: false with a careful event schema.

If that’s your full stack — Consent Mode v2 doesn’t apply to you, and most EU DPAs have signalled (informally, via FAQs and decisions like the Austrian DSB’s 2022 ruling) that consent-free analytics in this configuration is acceptable under GDPR Recital 47 (legitimate interest, properly balanced).

Read:  What Is Tracking in Web Analytics? A Simple Explanation

When you do need a Consent Mode-style pattern

Real stacks are rarely pure. Three common hybrid setups force you back into managing consent signals:

  1. Google Ads remarketing — the conversion pixel and remarketing tag are non-negotiable if you’re running paid campaigns. You need v2 signals (ad_user_data, ad_personalization) or your remarketing audiences quietly stop refreshing.
  2. Tag Manager firing third-party pixels — Meta Pixel, LinkedIn Insight, TikTok Pixel. Each has its own consent integration; GTM is the central control plane.
  3. reCAPTCHA Enterprise / Google Maps / YouTube embeds — these set Google cookies the moment the script loads. They’re not technically analytics, but they trip GDPR cookie rules.

For all three, you need a consent signal flow. The question is whether to use Google’s Consent Mode API, replicate it yourself, or replace it with a different abstraction.

Pattern 1: minimal hybrid — Plausible + Google Ads conversion

The most common hybrid stack we see. You measure with Plausible (cookieless, consent-free) and use Google Ads for paid acquisition. The conversion pixel still needs to fire on form submit / purchase, but only with the right consent signals.

The pattern: fire Plausible unconditionally on every page (legitimate interest, no PII), and gate the Google Ads conversion tag behind a single boolean ads_consent sourced from your CMP. Then forward that boolean into Consent Mode v2:

// Page bootstrap — before any tags
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }

// Default: deny ads/personalization, allow analytics (Plausible doesn't read this)
gtag('consent', 'default', {
  ad_storage: 'denied',
  ad_user_data: 'denied',
  ad_personalization: 'denied',
  analytics_storage: 'denied',
  wait_for_update: 500
});

// Plausible fires immediately — no consent needed
// <script defer data-domain="example.com" src="https://plausible.example.com/js/script.js"></script>

// CMP callback — after user clicks Accept
window.addEventListener('cmp:consent-given', (e) => {
  if (e.detail.purposes.advertising) {
    gtag('consent', 'update', {
      ad_storage: 'granted',
      ad_user_data: 'granted',
      ad_personalization: 'granted'
    });
  }
});

Two things this gets right that most production setups miss: the wait_for_update: 500 tells Google’s tags to wait up to 500ms for the consent signal before firing in default mode (avoids a race condition where the pixel fires “denied” then never gets the update). And analytics_storage stays denied throughout — you’re not running GA, so granting it does nothing useful and confuses audit logs.

Pattern 2: Matomo + Meta Pixel via your own tag dispatcher

If you’ve got a Tag Manager-style need (multiple pixels, conditional firing based on page type or user property) but don’t want Google’s GTM in the loop, you can build the same dispatcher yourself in <100 lines of JavaScript. The advantage: your consent logic is unified, you don’t need Consent Mode’s specific API surface, and your CSP doesn’t have to whitelist googletagmanager.com.

The skeleton: a single bootstrap script that exposes a tag() function. Tags register themselves and declare their consent requirement. The dispatcher reads consent state from a single source (your CMP), and fires only what’s allowed.

const Consent = {
  state: { analytics: 'denied', advertising: 'denied' },
  listeners: [],
  update(next) {
    this.state = { ...this.state, ...next };
    this.listeners.forEach(fn => fn(this.state));
  },
  subscribe(fn) { this.listeners.push(fn); fn(this.state); }
};

const Tags = {
  plausible() {
    // No consent gate — cookieless, legitimate interest
    const s = document.createElement('script');
    s.defer = true;
    s.src = 'https://plausible.example.com/js/script.js';
    s.setAttribute('data-domain', 'example.com');
    document.head.appendChild(s);
  },
  matomo() {
    // Cookieless Matomo also needs no gate
    /* ... */
  },
  metaPixel(consent) {
    if (consent.advertising !== 'granted') return;
    // Standard Meta Pixel init
    /* ... */
  }
};

// Bootstrap
Tags.plausible();
Consent.subscribe((c) => Tags.metaPixel(c));

// CMP wires into Consent.update({ advertising: 'granted' }) on Accept

This is what server-side advocates would call client-side first-party tagging. It does the consent dispatching that GTM does, but you own the code, the network calls go where you tell them to, and the CSP stays clean. Matomo fires unconditionally for analytics. The Meta Pixel is the only consent-gated tag, and consent state is observable from a single source.

Pattern 3: server-side dispatch with Cloudflare Worker

For the strictest setups — financial services, healthcare, anyone where a single third-party script on the page is a compliance risk — you push everything server-side. The browser sends one beacon to your domain. A Cloudflare Worker (or Hetzner-hosted reverse proxy) inspects the consent flags and fans out to vendors.

Read:  How to Audit Your Website Tracking in 30 Minutes

The trade-off is visible: you lose client-side context that some vendor tags depend on (browser fingerprinting for fraud detection, device-level identifiers for ad attribution). For Meta and Google Ads conversions, the Conversion API and Enhanced Conversions cover most of what the pixel did. For more exotic vendors, check before committing.

The pattern in pseudo-code:

// Client: single beacon, no third-party scripts
navigator.sendBeacon('/track', JSON.stringify({
  event: 'purchase',
  value: 49.00,
  consent: { analytics: true, advertising: true },
  // No PII, no email, no IP — the Worker enriches these
}));

// Cloudflare Worker
export default {
  async fetch(req) {
    const payload = await req.json();
    const ip = req.headers.get('CF-Connecting-IP');
    const ua = req.headers.get('User-Agent');

    const tasks = [];
    // Plausible — always
    tasks.push(forwardToPlausible(payload, ip, ua));
    // Google Ads CAPI — only if consent
    if (payload.consent.advertising) {
      tasks.push(forwardToGoogleAdsConversion(payload, ip, ua));
    }
    // Meta CAPI — only if consent
    if (payload.consent.advertising) {
      tasks.push(forwardToMetaCAPI(payload, ip, ua));
    }
    await Promise.all(tasks);
    return new Response('', { status: 204 });
  }
}

This is the strongest pattern from a privacy standpoint: third-party domains never see the user’s IP, browser, or referrer unless you choose to forward those fields. Your CSP can be locked to self and your own analytics domain. For the deeper version of this with a full Cloudflare Worker container, see our companion piece on self-hosted tag manager alternatives.

What you need to check against your CMP

Most paid CMPs (Cookiebot, OneTrust, Iubenda, Usercentrics) ship a Google Consent Mode integration out of the box. The integration handles the gtag('consent', ...) calls for you when the user clicks Accept/Reject. Three things to verify before trusting that integration:

  1. Default state is denied. Some integrations default to granted for the first page-load, which is illegal in the EU (no consent recorded yet means no consent — not implicit acceptance). Check your CMP’s debug panel: the first gtag('consent', 'default', ...) call must have all four parameters set to 'denied'.
  2. The update fires before any tag fires. If your Meta Pixel snippet runs before the CMP loads, you’re sending an unconsented hit and the Consent Mode update later is irrelevant — the data is already gone. Use wait_for_update and async-load your CMP at the top of <head>.
  3. Reject is honoured server-side too. If you have a Cloudflare Worker pattern or any server-side tagging, the consent flags need to travel with each beacon. A common bug: the CMP updates client-side gtag state, but the beacon to your worker uses a cached consent value from session storage and never refreshes.

What about Plausible Proxy + Consent Mode together?

A frequent question. Plausible’s proxy mode exists to bypass ad blockers and improve attribution, not to handle consent. Plausible’s proxy and Consent Mode v2 operate on different axes: the proxy is about where data goes (your domain instead of plausible.io), Consent Mode is about whether a Google tag fires. Running both is fine and they don’t interact. If you’re using the proxy, configure Plausible to fire on every page (no consent gate — cookieless analytics is consent-exempt under the analytics-only exemption recognised by most DPAs), and use Consent Mode purely for whatever Google touchpoints remain.

EU regulator stances at a glance

Regulator Position on consent-free analytics Position on Consent Mode v2 modeled data
CNIL (France) Accepted for cookieless tools meeting CNIL FAQ criteria (no fingerprinting, no cross-site identifier, retention limits) Modeled data must not be used for individual targeting; legitimate-interest basis questioned
BfDI (Germany) Accepted with technical conditions; states vary on enforcement Requires explicit consent for ad-personalization signals even in modeled form
DSB (Austria) Cookieless tools generally OK; Google Analytics struck down 2022 Has not specifically ruled on v2; pre-existing GA decisions still apply
Garante (Italy) Mixed — rulings have been site-by-site Has fined sites for misconfigured CMPs, no specific v2 ruling
AEPD (Spain) Cookieless analytics outside cookie law scope per AEPD 2020 guidance No specific v2 ruling; general TCF guidance applies
UK ICO UK GDPR retains most EU positions post-Brexit; ICO guidance recommends consent for non-essential cookies Engaged with Google directly on Consent Mode; no enforcement actions to date
Read:  Umami on Vercel + Neon: Free Self-Hosted Analytics in 5 Minutes

The pattern across all of them: cookieless self-hosted analytics is generally accepted. Consent Mode v2 modeled data is treated with suspicion but not banned. If you’re rolling out the patterns above, you’re in the safer half of the spectrum either way.

When NOT to use Consent Mode v2 at all

Three configurations where adding Consent Mode is either pointless or actively harmful:

  • You have no Google products in your stack. Plausible, Matomo, Umami, PostHog only. Consent Mode is a Google-tag feature — if no Google tag fires, the API does nothing. Adding gtag('consent', ...) calls to your bootstrap just adds dead weight to your page.
  • You’re in a strict consent jurisdiction with high-risk data. Modeled data (Consent Mode advanced) is exactly the thing some regulators dislike. If you’re a French publisher with a CNIL audit on the horizon, the safe play is Basic Mode only or no Google tag at all.
  • You haven’t tested the CMP integration end-to-end. A misconfigured Consent Mode is worse than no Consent Mode. The DPA-finding pattern is “site claimed consent management but defaulted to granted” — this is the most common GDPR fine pattern of the last 3 years.

FAQ

Do I need Consent Mode v2 if I only use Plausible / Matomo / Umami?

No. Consent Mode is a Google-product API. If no Google tag fires on your site, there’s nothing for the API to control. Cookieless analytics is consent-exempt in most EU jurisdictions under the cookie-law analytics exemption.

Does Consent Mode v2 work without a CMP?

Technically yes — you can call gtag('consent', ...) manually from your own consent banner. Practically, no — you need a UI to capture consent, a record of what was captured, and a way to honour withdrawal requests under GDPR Art 7(3). Building that from scratch is more work than buying a CMP.

What’s the difference between Consent Mode v1 and v2?

v2 adds two new parameters (ad_user_data and ad_personalization) on top of v1’s ad_storage and analytics_storage. The two new parameters were added to comply with the EU Digital Markets Act, which requires Google to obtain explicit consent before using ad-personalization signals. v1 is deprecated and Google Ads campaigns lose access to remarketing audiences from EU users if v1 is the only mode in place after March 2024.

Can I use Consent Mode v2 with server-side GTM?

Yes. The client-side gtag('consent', ...) calls travel with the hit to your server container, and downstream tags in the server container honour the consent flags. The benefit is that third-party domains never see the user directly — only your server container does. The trade-off is operational complexity (you now operate a sGTM container).

What happens to old data if I switch from Universal Analytics to a self-hosted tool mid-2024?

Universal Analytics data was readable from the GA4 interface until July 1, 2024 and exportable via BigQuery until then. After that date, the data is gone. For your self-hosted backlog, see our companion guide on self-hosted analytics alternatives — most tools have a GA import path for historical events.

Does the CNIL position differ from other EU regulators?

Yes, in degree. The CNIL has been the most vocal regulator on Consent Mode v2 modeled data, issuing several FAQs and informal guidance documents in 2024-2025. Other regulators (BfDI, DSB, AEPD) have been less specific but generally aligned. The UK ICO is somewhat looser post-Brexit but still expects valid consent for non-essential cookies. No regulator has banned Consent Mode v2 outright as of early 2026.

Next steps

Pick the pattern that matches your stack:

  • Pure self-hosted: you’re done. No Consent Mode needed.
  • Self-hosted + Google Ads only: Pattern 1, ~20 lines of bootstrap code, your CMP does the rest.
  • Multiple pixels (Meta, LinkedIn, TikTok): Pattern 2, build a unified dispatcher and skip GTM.
  • Strict compliance / financial / health: Pattern 3, server-side dispatch via Worker.

For the deeper deploy guides, see Plausible on Hetzner and Matomo on Hetzner. For the picker that walks through your stack constraints in 6 questions, head to /picker/.


Found this useful?

Try the Stack Picker to get a personal recommendation, or browse the install recipe library.