Cookie Consent & Tracking Setup
Ta treść nie jest jeszcze dostępna w Twoim języku.
Sellf ships with a built-in cookie consent system powered by vanilla-cookieconsent v3 (orestbida). It gates Google Tag Manager and Meta Pixel (incl. Conversions API) behind opt-in consent and wires Google Consent Mode V2. Umami Analytics runs cookieless and is not gated — see below. All settings live in the admin panel; no env vars required.
Compliance is configuration, not just code. GDPR/CCPA regulate behaviour, not which library you use. cookieconsent is a tool — the regulatory posture below depends on you keeping the defaults Sellf ships with.
- Default ALL OFF. Categories
analyticsandmarketingare opt-in (mode: 'opt-in', onlynecessaryisenabled:true readOnly:true). No tracking script may fire before the visitor explicitly accepts.- Every tracking script tagged. GTM + Meta Pixel render as
type="text/plain" data-category=... data-service=.... cookieconsent rewrites them to executable scripts only on opt-in. Any future tracking script you add MUST follow this convention or it will run unconditionally and break compliance.- Informed copy. The banner names each provider that is actually configured and its storage horizon (e.g. “Google Tag Manager — up to 24 months”, “Meta Pixel — ~7 days”). Generic copy (“we use cookies”) does not satisfy the GDPR Art. 7 “freely given, specific and informed” standard.
- Re-consent. The marketing footer (
LandingFooter) carries a “Cookie preferences” button (data-cc="show-preferencesModal") so visitors can withdraw consent as easily as they gave it (GDPR Art. 7(3)). Add the same button to any other globally visible chrome you ship.- Cookie policy in
/privacy. Sellf does NOT ship a privacy or cookie policy./privacyand/termsredirect to admin-suppliedPRIVACY_URL/TERMS_URL. You are responsible for publishing a cookie inventory (name, host, lifetime, purpose) there — the banner alone is not enough.
Umami exception. Umami runs in cookieless mode (no client cookies, anonymous visitor hashing). Under ePrivacy Art. 5(3) + GDPR recital 30, purely anonymous analytics that store nothing on the device are exempt from consent. Sellf therefore loads the Umami script unconditionally and it is not listed in the consent banner.
Table of Contents
Section titled “Table of Contents”- Architecture Overview
- Quick Setup
- Detailed Configuration
- How It Works
- Database Schema
- Common Scenarios
- Verification & Debugging
- Key Files
Architecture Overview
Section titled “Architecture Overview”Admin Panel (/dashboard/integrations) │ ▼integrations_config table (singleton row, id=1) │ ▼get_public_integrations_config() RPC (public, safe subset of config) │ ▼Root Layout (server component) fetches config │ ├─► TrackingProvider (injects scripts + cookieconsent) └─► TrackingConfigProvider (React context for event tracking hooks)All configuration is stored in the database and managed through the admin UI at /dashboard/integrations. No environment variables are needed for consent or tracking.
Quick Setup
Section titled “Quick Setup”- Go to
/dashboard/integrations→ Consents tab - Enable “Require Consent” → Save
- (Optional) Add tracking IDs in Analytics or Marketing tabs
- Done — cookieconsent banner appears on all pages
Detailed Configuration
Section titled “Detailed Configuration”1. Enable Consent Banner
Section titled “1. Enable Consent Banner”Admin Panel: /dashboard/integrations → Consents tab
| Setting | Effect |
|---|---|
| Require Consent | Shows cookieconsent banner, blocks non-essential scripts until user consents |
| Consent Logging | Logs each user’s consent choices to consent_logs table (for GDPR auditing) |
| Send conversions without consent | Sends Purchase/Lead events via CAPI even if user declines cookies (legitimate interest) |
When consent is enabled, tracking scripts are injected with type="text/plain" with data-category and data-service attributes. cookieconsent intercepts these and only converts them to executable JavaScript after the user consents to that specific service.
2. Meta Pixel & Conversions API
Section titled “2. Meta Pixel & Conversions API”Admin Panel: /dashboard/integrations → Marketing tab
| Field | Format | Where to find |
|---|---|---|
| Pixel ID | 10-20 digit number | Meta Events Manager → Settings |
| CAPI Token | Access token string | Events Manager → Settings → Generate Access Token |
| Test Event Code | TEST12345 | Events Manager → Test Events (for debugging) |
| Enable CAPI | Checkbox | Enables server-side conversion tracking |
What gets tracked (client-side, requires consent):
| User Action | Pixel Event | GTM Event |
|---|---|---|
| Views product page | ViewContent | view_item |
| Clicks “Buy” | InitiateCheckout | begin_checkout |
| Enters payment info | AddPaymentInfo | add_payment_info |
| Completes purchase | Purchase | purchase |
| Signs up / free product | Lead | generate_lead |
What gets tracked (server-side via CAPI):
PurchaseandLeadevents only- Bypasses adblockers
- Can work without cookie consent (see Conversions Without Consent)
- Deduplication with client-side Pixel via shared
event_id
3. Google Tag Manager
Section titled “3. Google Tag Manager”Admin Panel: /dashboard/integrations → Analytics tab
| Field | Format | Example |
|---|---|---|
| Container ID | GTM-XXXXXXX | GTM-ABC123 |
| Server Container URL | https://... (optional) | https://gtm.yourdomain.com |
The Server Container URL is for GTM Server-Side Tagging — it proxies tracking requests through your domain, bypassing most adblockers. Requires separate infrastructure (e.g., Stape.io or self-hosted).
Google Consent Mode V2 is automatically configured:
- Defaults to
deniedfor all consent types before cookieconsent loads - Updates to
grantedwhen user accepts via cookieconsent callback
4. Umami Analytics
Section titled “4. Umami Analytics”Admin Panel: /dashboard/integrations → Analytics tab
| Field | Format | Default |
|---|---|---|
| Website ID | UUID | — |
| Script URL | HTTPS URL | https://cloud.umami.is/script.js |
Umami is a privacy-focused alternative to Google Analytics. Sign up at umami.is.
5. Custom Scripts
Section titled “5. Custom Scripts”Admin Panel: /dashboard/integrations → Script Manager tab
Add any third-party script with:
- Location:
headorbody - Category:
essential(no consent needed),analytics, ormarketing(consent required) - Content: Raw
<script>content or external URL - Active toggle: Enable/disable without deleting
Non-essential scripts are automatically managed by cookieconsent — they only execute after user consent for the matching purpose.
6. Conversions Without Consent
Section titled “6. Conversions Without Consent”Admin Panel: /dashboard/integrations → Consents tab → “Send conversions without cookie consent”
When enabled:
- Purchase and Lead events are sent to Facebook CAPI even if the user declines cookies
- Uses hashed email + IP for matching (no
_fbc/_fbpcookies) - Legal basis: legitimate interest (GDPR Article 6(1)(f))
- All other events (ViewContent, InitiateCheckout, etc.) still require consent
Important: Your Privacy Policy must mention server-side conversion tracking under legitimate interest.
7. Consent Logging
Section titled “7. Consent Logging”When enabled, every interaction with the cookieconsent banner is logged via POST /api/consent:
{ "anonymous_id": "uuid", "consents": { "google-tag-manager": true, "facebook-pixel": false }, "consent_version": "1"}Stored in consent_logs table with IP, User-Agent, and timestamp. Rate limited to 30 requests/minute per IP.
How It Works
Section titled “How It Works”Consent Flow
Section titled “Consent Flow”User visits site → TrackingProvider renders in root layout → cookieconsent loads + shows banner (if no sellf_consent cookie) → Scripts injected as type="text/plain" (blocked)
User clicks "Accept All" → cookieconsent sets cookie: sellf_consent = {"categories":["necessary","analytics","marketing"], "services":{"analytics":["gtm"],"marketing":["pixel"]}, ...} → cookieconsent converts scripts from text/plain → text/javascript (executes them) → cookieconsent callback updates Google Consent Mode: analytics_storage → "granted" → POST /api/consent logs the choice (if consent_logging_enabled)
User clicks "Decline" / closes banner → Cookie set with all services = false → Scripts remain as text/plain (never execute) → CAPI still sends Purchase/Lead (if send_conversions_without_consent = true)Script Injection
Section titled “Script Injection”TrackingProvider (src/components/TrackingProvider.tsx) injects scripts in this order:
- Google Consent Mode V2 defaults (always, before everything else)
- cookieconsent config + library (if
cookie_consent_enabled) - GTM script (managed by cookieconsent if consent enabled)
- Meta Pixel script (managed by cookieconsent if consent enabled)
- Umami script (managed by cookieconsent if consent enabled)
- Custom scripts (managed by cookieconsent based on category)
All IDs are validated before injection to prevent XSS:
- GTM:
/^GTM-[A-Z0-9]{1,10}$/i - Pixel:
/^\d{10,20}$/ - Umami: UUID format
- URLs: Must start with
https://
Google Consent Mode V2
Section titled “Google Consent Mode V2”When GTM is enabled, the following is set before GTM loads:
gtag('consent', 'default', { 'ad_storage': 'denied', 'ad_user_data': 'denied', 'ad_personalization': 'denied', 'analytics_storage': 'denied', 'wait_for_update': 500});After user interacts with cookieconsent, the callback updates consent:
gtag('consent', 'update', { 'analytics_storage': consent['google-tag-manager'] ? 'granted' : 'denied', 'ad_storage': consent['facebook-pixel'] ? 'granted' : 'denied', 'ad_user_data': consent['facebook-pixel'] ? 'granted' : 'denied', 'ad_personalization': consent['facebook-pixel'] ? 'granted' : 'denied',});Server-Side Conversions (CAPI)
Section titled “Server-Side Conversions (CAPI)”Facebook Conversions API sends events server-to-server, bypassing adblockers:
Client: trackEvent('purchase', data) → POST /api/tracking/fb-capi → Server checks: fb_capi_enabled? capi_token exists? → Server checks: hasConsent OR send_conversions_without_consent? → If yes: POST https://graph.facebook.com/v18.0/{pixel_id}/eventsWith consent: sends full data including _fbc, _fbp cookies for better matching.
Without consent: sends hashed email + IP only (lower match rate but still valuable).
Event Tracking
Section titled “Event Tracking”Client-side tracking (src/lib/tracking/client.ts) provides:
trackEvent(eventName, data, config)Before sending, it checks cookieconsent consent:
hasFacebookConsent()→ readssellf_consentcookie forfacebook-pixelhasGTMConsent()→ readssellf_consentcookie forgoogle-tag-manager
Events are pushed to:
- GTM
dataLayer(if GTM enabled + consent) fbq('track', ...)(if Pixel enabled + consent)/api/tracking/fb-capi(if CAPI enabled, consent rules apply server-side)
Deduplication: each event gets a UUID event_id shared between Pixel and CAPI.
Database Schema
Section titled “Database Schema”integrations_config (singleton, id=1)
Section titled “integrations_config (singleton, id=1)”| Column | Type | Purpose |
|---|---|---|
gtm_container_id | TEXT | GTM Container ID |
gtm_server_container_url | TEXT | GTM Server URL |
facebook_pixel_id | TEXT | Meta Pixel ID |
facebook_capi_token | TEXT | Meta CAPI access token |
facebook_test_event_code | TEXT | Meta test event code |
fb_capi_enabled | BOOLEAN | Enable server-side conversions |
send_conversions_without_consent | BOOLEAN | Allow CAPI without consent |
umami_website_id | TEXT | Umami Website UUID |
umami_script_url | TEXT | Umami script URL |
cookie_consent_enabled | BOOLEAN | Show cookieconsent banner |
consent_logging_enabled | BOOLEAN | Log consent to DB |
sellf_license | TEXT | License key (unrelated) |
custom_scripts
Section titled “custom_scripts”| Column | Type | Purpose |
|---|---|---|
id | UUID | Primary key |
name | TEXT | Display name |
script_location | TEXT | head or body |
script_content | TEXT | Script code or URL |
category | TEXT | essential, analytics, or marketing |
is_active | BOOLEAN | Enable/disable |
cookie_consent_enabled | BOOLEAN | Whether cookieconsent manages this script |
consent_logs
Section titled “consent_logs”| Column | Type | Purpose |
|---|---|---|
id | UUID | Primary key |
user_id | UUID | Authenticated user (nullable) |
anonymous_id | TEXT | Anonymous visitor ID |
ip_address | TEXT | Visitor IP |
user_agent | TEXT | Browser User-Agent |
consents | JSONB | {"service-name": true/false} |
consent_version | TEXT | Config version |
created_at | TIMESTAMPTZ | Timestamp |
Common Scenarios
Section titled “Common Scenarios”| Scenario | GTM | GTM Server | Pixel | CAPI | Umami | Consent |
|---|---|---|---|---|---|---|
| Meta Pixel only | - | - | Yes | Yes | - | Yes |
| Privacy-focused (no Big Tech) | - | - | - | - | Yes | Yes |
| Full Google ecosystem | Yes | - | Yes | Yes | - | Yes |
| Adblocker resistant | Yes | Yes | Yes | Yes | - | Yes |
| All features | Yes | Yes | Yes | Yes | Yes | Yes |
| No tracking (consent banner only) | - | - | - | - | - | Yes |
Verification & Debugging
Section titled “Verification & Debugging”Check the banner appears
Section titled “Check the banner appears”Visit your site in an incognito window — the cookieconsent banner should appear at the bottom.
Check consent cookie
Section titled “Check consent cookie”Browser DevTools → Application → Cookies → look for sellf_consent:
{"google-tag-manager":true,"facebook-pixel":true,"umami-analytics":true}Check scripts are blocked/unblocked
Section titled “Check scripts are blocked/unblocked”DevTools → Elements → search for text/plain:
- Before consent: tracking scripts have
type="text/plain" - After consent: scripts have
type="text/javascript"(or no type attribute)
Check Meta Events Manager
Section titled “Check Meta Events Manager”Events Manager → Test Events → enter your Test Event Code → trigger events on your site. Events should appear within seconds.
Check consent logs
Section titled “Check consent logs”SELECT anonymous_id, consents, created_atFROM consent_logsORDER BY created_at DESCLIMIT 10;Debug tracking events
Section titled “Debug tracking events”Browser DevTools → Console → filter for [tracking] or [fb-capi] log messages.
Key Files
Section titled “Key Files”| File | Purpose |
|---|---|
src/components/TrackingProvider.tsx | Script injection + cookieconsent configuration |
src/components/IntegrationsForm.tsx | Admin UI for all integrations settings |
src/lib/actions/integrations.ts | Server actions (get/update config, CRUD scripts) |
src/lib/validations/integrations.ts | Input validation for all tracking IDs/URLs |
src/lib/tracking/client.ts | Client-side event tracking + consent checks |
src/lib/tracking/server.ts | Server-side CAPI tracking |
src/app/api/consent/route.ts | Consent logging endpoint |
src/app/api/tracking/fb-capi/route.ts | Facebook CAPI proxy endpoint |
src/app/layout.tsx | Root layout (fetches config, renders providers) |
tests/helpers/consent.ts | Test helper for bypassing consent banner |