← knowledge.oriz.in

Consent management for many categories — Klaro config + GA4 Consent Mode v2 + geo routing + cookie-less default

decision securityprivacyconsentklarogdprccpagpccookiesmulti-categorygeo

Consent management for many categories — Klaro config + GA4 Consent Mode v2 + geo routing + cookie-less default

Decision

The family's consent surface uses 5 categories × N services via Klaro, with three orthogonal levers stacked on top:

  1. Geo-routed defaults — EU/UK gets default-DENIED banner; US/CA gets default-ACCEPTED with Sec-GPC honoured; rest of world gets NO banner.
  2. Lazy-loaded Klaro itself — Klaro JS ships ONLY to visitors whose CF-IPCountry is in the EU/UK/CCPA list. Other visitors get zero CLS, zero render-block, zero Klaro bytes.
  3. Cookie-less defaults — every service that has a cookie-less mode uses it by default, so the consent surface stays small.

This refines, not supersedes, security/cookie-banner-policy.md — that policy stated "no banner unless EU + tracker"; this decision adds the explicit category map, the US/CA + GPC handling, the lazy- load rule, and the cookie-less default rule.

Categories (Klaro purposes array)

Category What it covers Default consent
necessary Auth session (Firebase Auth), CSRF tokens, session cookies, transactional notification routing Always on, no consent UI shown
analytics GA4, PostHog (autocapture mode), Microsoft Clarity, Sentry user-context, Algolia Insights Geo-default (see Geo-routing below)
marketing UTM persistence cookie, email-marketing UTM tracking, Razorpay cart UUID Geo-default
functional Theme preference, language preference, font-size preference, FCM push opt-in Geo-default
social Giscus comment cookie, Bluesky / AT Protocol auth tokens for lifestream embed Off until user-clicked

Services map (Klaro services array)

Service Category Pre-consent posture Notes
Cloudflare Web Analytics necessary Loaded always Cookie-less by design; documented under necessary for legal clarity
Sentry necessary (default) ? analytics if user-PII captured Loaded with PII off by default Default Sentry config does NOT capture PII; if a site flips on user-context, the entry moves to analytics for that site
Google Analytics 4 analytics Loaded in denied mode via GA4 Consent Mode v2 Script loads, but tags fire only after gtag('consent', 'update', { analytics_storage: 'granted' })
PostHog analytics capture_pageview: false, persistence: 'memory' until consent After consent, persistence flips to localStorage; PostHog keeps anonymous mode by default per its service file
Microsoft Clarity analytics + marketing Blocked until consent Session-recording is the most sensitive surface; gated to both categories so denying either suppresses Clarity entirely
Knock necessary Loaded always Server-side transactional notifications; no client cookies
FCM (web push) functional Browser permission prompt only after user clicks "Enable notifications" Consent for FCM is the OS-level Notifications permission, not a Klaro toggle; the toggle just shows the in-app prompt button
Giscus social Loaded only after consent OR user-click on a "Load comments" placeholder Lazy-loaded iframe; placeholder visible until clicked
Algolia Insights analytics Disabled until consent Algolia search itself is necessary; the Insights events client is gated separately
UTM persistence cookie marketing Set only after consent (EU); set immediately (US/CA pre-GPC); never set (rest, falls through to URL-only) Documented in utm-attribution-strategy.md
Razorpay cart UUID marketing Set on checkout-page entry; outside Klaro scope (necessary for the checkout flow) but flagged for legal clarity Razorpay's own SDK manages this; the family doesn't override
Theme / language / font-size prefs functional Set immediately, anywhere (low-sensitivity preferences) Pre-existing user expectation that prefs persist; falls under "strictly necessary for the requested feature"

Geo-routing rule

The CF edge reads CF-IPCountry on every request and emits a tiny inline <script> that sets window.__consentRegime before any tracker loads:

Visitor region __consentRegime Banner shown? Default consent Auto-honour
EU member state, UK, IS, NO, LI, CH 'eu' Yes DENIED for analytics / marketing / functional / social
US, CA 'ccpa' Yes (CCPA "Do Not Sell" link required) ACCEPTED Sec-GPC: 1 request header ? auto-DENY analytics + marketing
Rest of world 'rest' No banner Trackers load if locally lawful (cookie-less default services always; cookie-issuing services per local law)

Sec-GPC: 1 (Global Privacy Control) is honoured on every CCPA- region request: when the header is present, Klaro pre-fills the banner with analytics + marketing toggles OFF, equivalent to a CCPA "Do Not Sell or Share" opt-out. The visitor can still re-enable via the banner — GPC is the default, not a hard gate.

Lazy-load rule

Klaro's JS bundle (~17 KB gzipped from jsDelivr) ships only to visitors whose CF-IPCountry is in the union of EU + UK + Iceland + Norway + Liechtenstein + Switzerland + US + CA. Other visitors:

The CF edge emits a tiny inline <script> that conditionally injects the Klaro <script> tag. The shared <ConsentBanner> helper in @chirag127/oriz-kit (forward reference) ships this gate so no site re-implements it.

This refines cookie-banner-policy.md: that policy gated Klaro on (EU visitor) × (cookie-issuing tracker on this page); this decision widens the visitor list to cover CCPA regions and codifies the lazy-load JS-loading rule.

Cookie-less default rule

For services that have a cookie-less mode, USE IT by default:

Service Cookie-less mode Default?
Cloudflare Web Analytics Cookie-less by design Yes
Sentry sendDefaultPii: false (default) Yes
Cloudflare Pages basic auth (where used) Token-based, no cookie Yes
PostHog persistence: 'memory' pre-consent Yes (until consent flips it)
reCAPTCHA Enterprise Cookie-less assessment mode Yes (Firestore-write surface only)
Cloudflare Turnstile Cookie-less by design Yes

The smaller the cookie-issuing surface, the smaller the consent UI the family ever has to ship.

Why

Implications

Cross-refs