ConsentPixel — Privacy · Verified
One <script> tag in your <head>. Full CIPA, GDPR, CCPA, and US state law compliance — automatic. No plugins, no dependencies, no page reload required. Load time under 50ms from Cloudflare's global edge.
WordPress, Shopify, Webflow, Next.js, Squarespace, Wix, raw HTML — or any other framework. The pixel is framework-agnostic vanilla JS. If you can add a <script> tag to your site's <head>, you can install ConsentPixel.
How it works
The ConsentPixel pixel does five things in sequence on every page load:
- Blocks all non-essential scripts before the browser can execute them — MutationObserver intercept rewrites
type="text/javascript"totext/plainon any script not categorised as strictly necessary. - Fetches your banner configuration from Cloudflare's edge network in under 50ms — your branding, categories, language settings.
- Detects the visitor's jurisdiction from Cloudflare request headers — GDPR for EU/EEA/UK, CCPA for California, state-specific rules for VA/CO/TX/FL/CT.
- Renders the consent banner in a Shadow DOM — fully isolated from your site's styles, keyboard-navigable, WCAG 2.1 AA accessible.
- On consent decision — restores blocked scripts per category, fires Google Consent Mode v2 signals, posts an immutable consent log to your dashboard.
Prerequisites
- A ConsentPixel account — sign up free (14-day trial, no card)
- Your Site ID — found in your portal under Settings → Sites → Your site → Pixel snippet
- Ability to edit your site's
<head>tag
The pixel must load in the <head> tag — before any other scripts — to block trackers before the browser executes them. Installing in the footer or after other scripts defeats the CIPA protection entirely. The script is async so it will not block page rendering.
Install the pixel in 4 steps
From zero to full compliance in under 10 minutes on any platform.
SITE_ID is pre-filled:<!-- ConsentPixel — Privacy · Verified --> <script src="https://cdn.consentpixel.com/pixel.min.js" data-site-id="YOUR_SITE_ID" async ></script>
<head> tag — before any other scripts, stylesheets, or third-party tags. This is critical: the pixel must run before any other scripts to intercept them.<html> <head> <!-- ConsentPixel FIRST — before all other scripts --> <script src="https://cdn.consentpixel.com/pixel.min.js" data-site-id="YOUR_SITE_ID" async></script> <!-- All other scripts AFTER --> <script src="https://www.googletagmanager.com/gtag/js" async></script> <!-- ... rest of <head> ... --> </head>
Optional configuration attributes
The pixel script tag accepts optional data-* attributes to override defaults:
| Attribute | Type | Default | Description |
|---|---|---|---|
| data-site-id | string | — | Required Your site's unique identifier from the portal. |
| data-mode | string | "auto" | Regulation mode. Options: auto | gdpr | ccpa | cipa. Auto detects per visitor location. |
| data-language | string | "auto" | Banner language. Auto detects from browser headers. Override with ISO 639-1 code e.g. en, de, fr. |
| data-block-on-load | boolean | true | Whether to block non-essential scripts on load. Set false only if you manage blocking manually via the JS API. |
| data-gpc-respect | boolean | true | Whether to honour the Global Privacy Control signal. Required for CCPA 2026 compliance — do not disable. |
| data-consent-expiry | number | 365 | Days before re-prompting for consent. Minimum 90 for GDPR. Maximum 395. |
Verify your installation
Three ways to confirm the pixel is installed correctly and blocking pre-consent tracking.
Method 1 — Portal auto-detection
After installing the pixel, load any page on your site in a browser. Within 2–5 minutes, your ConsentPixel portal will show a green ✓ next to your site. If you see a yellow ⚠ after 10 minutes, check that the pixel is in <head> and the data-site-id matches your portal site ID exactly.
Method 2 — Browser network inspector
Open your site in a fresh private/incognito window. Open DevTools → Network tab. Load the page. Look for a request to cdn.consentpixel.com/pixel.min.js — it should be one of the first requests. Then look at the request timing for any third-party trackers — they should show either blocked status or fire only after a consent event.
Method 3 — Browser console
// Paste in browser console on your site const cp = window.ConsentPixel; if (cp) { console.log('✓ Pixel loaded — version:', cp.version()); console.log('Current consent:', cp.getConsent()); console.log('Regulation detected:', cp.getRegulation()); console.log('Blocked scripts:', cp.getBlockedScripts()); } else { console.error('✗ ConsentPixel not found — check <head> installation'); }
✓ Pixel loaded — version: 1.2.0
Current consent: { analytics: false, marketing: false, functional: true }
Regulation detected: "gdpr" (or ccpa / cipa depending on visitor location)
Blocked scripts: ["hotjar.com", "static.hotjar.com", "clarity.ms", ...]
Platform-specific installation
Step-by-step guides for the most common platforms. The pixel code is identical across all platforms — only where you paste it differs.
WordPress
Recommended: Use the theme's header. This is faster and more reliable than a plugin.
- Go to Appearance → Theme File Editor (or Appearance → Theme Editor).
- Select
header.phpfrom the file list on the right. - Find the opening
<head>tag and paste the ConsentPixel snippet immediately after it — before any other scripts. - Click Update File.
Always edit the child theme's header.php, not the parent theme. Parent theme updates will overwrite changes to the parent's files. If your child theme doesn't have a header.php, copy it from the parent theme first.
Alternative — wp_head hook: Add this to your child theme's functions.php:
function consentpixel_head() { echo '<script src="https://cdn.consentpixel.com/pixel.min.js" data-site-id="YOUR_SITE_ID" async></script>'; } add_action( 'wp_head', 'consentpixel_head', 1 );
Priority 1 ensures ConsentPixel loads before any other wp_head hooks. Do not use priority 10 (default) — other plugins will load their scripts before ConsentPixel.
Shopify
- In your Shopify admin, go to Online Store → Themes.
- Click Actions → Edit code on your active theme.
- Open
layout/theme.liquid. - Find the
<head>tag and paste the ConsentPixel snippet as the very first line inside it. - Click Save.
<head> {%- comment -%} ConsentPixel — must be first {%- endcomment -%} <script src="https://cdn.consentpixel.com/pixel.min.js" data-site-id="YOUR_SITE_ID" async ></script> {%- render 'head-tags' -%} {{- content_for_header -}} <!-- ... rest of head ... -->
Shopify's checkout pages (/checkout, /thank_you) require a Shopify Plus plan to inject custom head scripts. On non-Plus plans, use Shopify's Customer Privacy API alongside ConsentPixel on non-checkout pages. Contact support@consentpixel.com for the Shopify Plus checkout guide.
Webflow
- In the Webflow Designer, open Project Settings → Custom Code.
- In the Head Code field, paste the ConsentPixel snippet.
- Click Save Changes then Publish your site.
Webflow's Head Code field loads before all other scripts — ideal for ConsentPixel. No additional configuration needed for standard Webflow sites.
<!-- ConsentPixel — Privacy · Verified --> <script src="https://cdn.consentpixel.com/pixel.min.js" data-site-id="YOUR_SITE_ID" async ></script>
Next.js
Use Next.js's built-in <Script> component with strategy="beforeInteractive" to ensure the pixel loads before any other scripts — including those injected by the Next.js runtime.
import Script from 'next/script'; export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html> <head> {/* ConsentPixel — must use beforeInteractive */} <Script src="https://cdn.consentpixel.com/pixel.min.js" data-site-id={process.env.NEXT_PUBLIC_CONSENTPIXEL_SITE_ID} strategy="beforeInteractive" /> </head> <body>{children}</body> </html> ); }
Add your site ID to .env.local:
NEXT_PUBLIC_CONSENTPIXEL_SITE_ID=your_site_id_here
Add the Script component to pages/_document.tsx inside the <Head> component from next/document, with strategy="beforeInteractive". Do not use _app.tsx — it loads too late.
Squarespace
- Go to Website → Pages → Website Tools → Code Injection.
- In the HEADER field, paste the ConsentPixel snippet.
- Click Save.
Code Injection requires a Business plan or higher. The Personal plan does not support custom code injection. If you're on Personal, upgrade or contact us about the Squarespace-specific workaround via our agency partners.
<script src="https://cdn.consentpixel.com/pixel.min.js" data-site-id="YOUR_SITE_ID" async ></script>
Wix
- In your Wix dashboard, go to Settings → Custom Code.
- Click + Add Custom Code.
- Paste the ConsentPixel snippet.
- Set Place Code in →
Head. - Set Add Code to Pages →
All Pages. - Set Load Code Once →
true. - Click Apply.
Wix does not guarantee script load order within the head. If you notice analytics firing before consent, enable the data-block-on-load attribute and contact support@consentpixel.com for the Wix-specific configuration guide that works around this limitation.
Google Consent Mode v2
ConsentPixel — Privacy · Verified automatically implements Google Consent Mode v2. No additional configuration is required if you've installed the pixel before your GA4 or GTM tags. This section explains what happens and how to verify it.
How ConsentPixel handles Consent Mode v2
On every page load, ConsentPixel injects a gtag('consent', 'default', ...) call into the dataLayer before Google Tag Manager or GA4 loads. All four v2 signals are set to 'denied' by default. When the visitor makes a consent decision, ConsentPixel fires gtag('consent', 'update', ...) with the appropriate signal values.
Automatic signal mapping
ConsentPixel maps your consent categories to Consent Mode v2 signals automatically:
| Visitor consents to | Signals set to granted |
|---|---|
| Analytics category | analytics_storage: 'granted' |
| Marketing category | ad_storage: 'granted', ad_user_data: 'granted', ad_personalization: 'granted' |
| Accept all | All four signals set to 'granted' |
| Decline all / GPC signal | All four signals remain 'denied' |
Manual signal control (advanced)
You can override the automatic mapping using the JavaScript API:
// Override GCM signals manually (advanced use only) window.ConsentPixel.setConsentModeSignals({ ad_storage: 'denied', analytics_storage: 'granted', ad_user_data: 'denied', ad_personalization: 'denied', }); // Listen for consent updates and sync to GCM window.ConsentPixel.on('consent:update', (decision) => { // decision.categories: { analytics: bool, marketing: bool, functional: bool } // ConsentPixel handles GCM update automatically — this is for custom logic console.log('Consent updated:', decision); });
Verify Consent Mode is active
In Google Tag Manager, add a GA4 Configuration tag and enable Require additional consent for ad measurement. Then use the GTM Preview mode — you should see consent state in the dataLayer events before any GA4 events fire.
In GA4, go to Admin → Data Streams → your stream → Consent settings. If Consent Mode is active, you'll see the modelled conversion data toggle available.
Google Tag Manager integration
ConsentPixel — Privacy · Verified intercepts the GTM dataLayer before GTM loads, injecting consent state at the source. Tags configured with consent checks in GTM will respect ConsentPixel decisions automatically.
Step 1 — Load order
ConsentPixel must load before GTM. Verify your <head> order:
<head> <!-- 1. ConsentPixel FIRST --> <script src="https://cdn.consentpixel.com/pixel.min.js" data-site-id="YOUR_SITE_ID" async></script> <!-- 2. GTM second --> <script> (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-XXXXXXX'); </script> </head>
Step 2 — Configure consent in GTM
In GTM, go to Admin → Container Settings → Additional Settings and enable Enable consent overview. Then for each tag, click the shield icon (🛡) in the tag editor and configure which consent type must be granted before the tag fires:
- GA4 — requires
analytics_storage: granted - Google Ads — requires
ad_storage: grantedandad_user_data: granted - Meta Pixel — requires
ad_storage: granted - Hotjar / FullStory / Clarity — requires
analytics_storage: granted(or block entirely at pixel level — recommended for CIPA)
Step 3 — Test with GTM Preview
Open GTM → Preview mode → visit your site. In the dataLayer panel, you should see a consent_default event fired by ConsentPixel before any other events. After you interact with the consent banner, you should see a consent_update event with the updated signal values.
If Hotjar or Clarity are loaded via GTM, configuring GTM consent checks is not sufficient for CIPA protection — GTM itself fires before consent and can still load scripts. ConsentPixel's MutationObserver intercept blocks the GTM container's injected scripts too, but only if ConsentPixel loads before GTM. Verify load order is correct using the browser console method in the Verify Install section.
window.ConsentPixel API Reference
After the pixel loads, window.ConsentPixel exposes a stable public API for reading consent state, responding to consent events, and programmatically controlling the banner.
The window.ConsentPixel object is available as soon as the pixel loads. Because the script is async, check availability before calling API methods, or listen for the ConsentPixel:ready event on window.
Core methods
| Method | Returns | Description |
|---|---|---|
| .version() | string | Returns the current pixel version string. e.g. "1.2.0" |
| .getConsent() | ConsentState | Returns the visitor's current consent decision object. Null if no decision has been made yet. |
| .getRegulation() | Regulation | Returns the detected regulation for this visitor. One of "gdpr" | "ccpa" | "cipa" | "none". |
| .getGeo() | GeoInfo | Returns the visitor's detected country and region codes used for regulation detection. |
| .hasConsented(category) | boolean | Returns true if the visitor has consented to the given category. Category: "analytics" | "marketing" | "functional". |
| .getBlockedScripts() | string[] | Returns array of third-party domains currently being blocked pending consent. |
| .getBannerVersion() | string | Returns the version identifier of the banner currently shown to this visitor. Used in consent logs. |
Banner control methods
| Method | Returns | Description |
|---|---|---|
| .showBanner() | void | Programmatically shows the consent banner. Useful for "Manage preferences" links in your footer. |
| .hideBanner() | void | Hides the consent banner. Only use if implementing a fully custom consent UI. |
| .acceptAll() | Promise<void> | Programmatically accepts all categories. Posts consent log. Re-enables all blocked scripts. Updates GCM signals. |
| .declineAll() | Promise<void> | Programmatically declines all non-essential categories. Posts consent log. All trackers remain blocked. |
| .setConsent(categories) | Promise<void> | Sets consent for specific categories. Accepts a partial ConsentCategories object. |
| .resetConsent() | Promise<void> | Clears the stored consent decision and re-shows the banner. Use for "Withdraw consent" UI. |
Event methods
| Method | Returns | Description |
|---|---|---|
| .on(event, handler) | void | Registers an event listener. See Events section for all event types. |
| .off(event, handler) | void | Removes a previously registered event listener. |
| .once(event, handler) | void | Registers a one-time event listener that fires at most once then removes itself. |
Advanced methods
| Method | Returns | Description |
|---|---|---|
| .setConsentModeSignals(signals) | void | Manually override Google Consent Mode v2 signals. See GCM section for signal names. |
| .getConsentLog() | ConsentLogEntry | Returns the most recent consent log entry for this visitor — timestamp, decision, banner version, regulation. |
| .isGPCActive() | boolean | Returns true if the visitor has Global Privacy Control enabled in their browser. |
Usage examples
// Wait for ConsentPixel to be ready window.addEventListener('ConsentPixel:ready', () => { const cp = window.ConsentPixel; // Check if visitor has consented to analytics if (cp.hasConsented('analytics')) { // Load your own analytics code } // React to consent changes cp.on('consent:update', (decision) => { if (decision.categories.analytics) { // User accepted analytics — initialise your tool } }); }); // Add "Manage preferences" link to footer document.getElementById('manage-cookies') .addEventListener('click', () => { window.ConsentPixel.showBanner(); });
TypeScript type definitions
Full TypeScript type definitions for the window.ConsentPixel API. Add these to a .d.ts file in your project for full type safety.
/** ConsentPixel — Privacy · Verified — Type Definitions v1.2 */ declare global { interface Window { ConsentPixel: ConsentPixelAPI; } } type Regulation = 'gdpr' | 'ccpa' | 'cipa' | 'none'; type ConsentCategory = 'functional' | 'analytics' | 'marketing'; type GCMSignalValue = 'granted' | 'denied'; interface ConsentCategories { functional: boolean; // always true — strictly necessary analytics: boolean; marketing: boolean; } interface ConsentState { categories: ConsentCategories; timestamp: string; // ISO 8601 bannerVersion: string; regulation: Regulation; isGPCActive: boolean; method: 'explicit' | 'gpc' | 'implied'; } interface GeoInfo { country: string; // ISO 3166-1 alpha-2 e.g. "US" region: string | null; // US state code e.g. "CA", null for non-US regulation: Regulation; } interface GCMSignals { ad_storage: GCMSignalValue; analytics_storage: GCMSignalValue; ad_user_data: GCMSignalValue; ad_personalization: GCMSignalValue; } interface ConsentDecisionEvent { categories: ConsentCategories; previousCategories: ConsentCategories | null; regulation: Regulation; gcmSignals: GCMSignals; timestamp: string; } interface ConsentLogEntry { visitorId: string; // SHA-256 hashed — not a raw identifier siteId: string; timestamp: string; bannerVersion: string; regulation: Regulation; geo: { country: string; region: string | null }; decision: ConsentCategories; } type ConsentPixelEvent = | 'consent:update' // visitor made or changed consent decision | 'consent:reset' // consent was cleared / banner re-shown | 'banner:show' // banner became visible | 'banner:hide' // banner was dismissed | 'script:unblocked' // a script was re-enabled after consent; interface ConsentPixelAPI { // Core version(): string; getConsent(): ConsentState | null; getRegulation(): Regulation; getGeo(): GeoInfo; hasConsented(category: ConsentCategory): boolean; getBlockedScripts(): string[]; getBannerVersion(): string; isGPCActive(): boolean; // Banner control showBanner(): void; hideBanner(): void; acceptAll(): Promise<void>; declineAll(): Promise<void>; setConsent(categories: Partial<ConsentCategories>): Promise<void>; resetConsent(): Promise<void>; // Events on<E extends ConsentPixelEvent>( event: E, handler: (data: ConsentDecisionEvent) => void ): void; off<E extends ConsentPixelEvent>( event: E, handler: (data: ConsentDecisionEvent) => void ): void; once<E extends ConsentPixelEvent>( event: E, handler: (data: ConsentDecisionEvent) => void ): void; // Advanced setConsentModeSignals(signals: Partial<GCMSignals>): void; getConsentLog(): ConsentLogEntry | null; } export {}; // make this a module
ConsentPixel events
Subscribe to consent lifecycle events using window.ConsentPixel.on(). All events are dispatched on the window.ConsentPixel event emitter and also as native CustomEvent on window with the prefix ConsentPixel:.
| Event name | When fired | Event data |
|---|---|---|
| consent:update | Any time the visitor makes or changes their consent decision — initial decision, preference update, or GPC detection | ConsentDecisionEvent |
| consent:reset | When resetConsent() is called or consent expires and is cleared | { timestamp: string } |
| banner:show | When the consent banner becomes visible — initial load or showBanner() call | { regulation: Regulation, bannerVersion: string } |
| banner:hide | When the banner is dismissed (any action) or hidden via hideBanner() | { action: 'accept' | 'decline' | 'partial' | 'close' } |
| script:unblocked | When a previously blocked script is re-enabled after consent | { domain: string, category: ConsentCategory } |
Listening via native CustomEvent
// Via ConsentPixel event emitter (recommended) window.ConsentPixel.on('consent:update', (e) => { console.log('Analytics consented:', e.categories.analytics); console.log('Marketing consented:', e.categories.marketing); console.log('GCM signals:', e.gcmSignals); }); // Via native window CustomEvent (alternative) window.addEventListener('ConsentPixel:consent:update', (e) => { const decision = e.detail; // same shape as ConsentDecisionEvent });
DSAR form embed
Embed a Data Subject Access Request form on any page of your site. Submissions are routed directly to your ConsentPixel portal's DSAR inbox, where you can track status, set deadlines, and export responses for legal compliance.
Embed code
Paste this snippet anywhere in your page body where you want the DSAR form to appear — typically on your Privacy Policy page or a dedicated "Your Privacy Rights" page.
<!-- ConsentPixel DSAR Form --> <div id="cp-dsar-form" data-site-id="YOUR_SITE_ID" data-lang="auto" data-theme="light" ></div> <script src="https://cdn.consentpixel.com/dsar-form.js" async ></script>
Form preview
This is how the embedded form appears to your visitors:
We will respond within 30 days (GDPR) or 45 days (CCPA). Your request is processed securely by ConsentPixel — Privacy · Verified. Privacy Policy.
Configuration options
| Attribute | Type | Default | Description |
|---|---|---|---|
| data-site-id | string | — | Required Your ConsentPixel site ID. |
| data-lang | string | "auto" | Form language. Auto-detects from browser. Override with ISO 639-1 code. |
| data-theme | string | "light" | Visual theme. Options: light | dark | system. |
| data-accent-color | string | "#00B896" | CSS hex colour for the submit button and focus rings. Match your brand. |
| data-show-types | string | "all" | Comma-separated list of request types to show. e.g. "access,deletion,portability". |
| data-redirect-url | string | — | URL to redirect to after successful submission. If not set, shows an inline success message. |
Handling submissions in the portal
Submitted requests appear in your ConsentPixel portal under Compliance → DSAR Requests. Each request shows:
- Requester name and email
- Request type and any additional details
- Submission timestamp and applicable regulation
- Response deadline (auto-calculated — 30 days GDPR, 45 days CCPA)
- Status workflow:
Received → In review → Responded → Closed
All DSAR activity is logged to an immutable audit trail for regulatory compliance purposes.
Privacy Verified trust badge
Display a cryptographically verified compliance badge on your site. The badge reflects your real-time compliance status — green when clean, amber when action is needed. It cannot be faked: the badge is served dynamically from our CDN and validates your domain on every request.
Badge embed code
<!-- ConsentPixel Privacy Verified badge --> <a href="https://verify.consentpixel.com/YOUR_SITE_ID" target="_blank" rel="noopener" > <img src="https://badge.consentpixel.com/YOUR_SITE_ID" alt="Privacy Verified by ConsentPixel" width="160" height="40" /> </a>
How the badge is verified
The badge image is served by a Cloudflare Worker that checks your site's current compliance status on every request. The badge URL includes an HMAC-SHA256 token tied to your site ID and domain — any attempt to use the badge on a different domain returns a fraud-indicator badge automatically.
The badge reflects your most recent scan result: green (A/B grade), amber (C/D grade or outstanding alert), or red (F grade or domain mismatch). It updates automatically when your next scan completes — no code change needed.
Pixel changelog
All notable changes to the ConsentPixel pixel. The pixel is delivered via CDN — updates deploy automatically. Breaking changes are announced with 30 days notice.
v1.2.0 — May 2026
- New:
window.ConsentPixel.isGPCActive()method — query GPC status directly - New:
script:unblockedevent — know exactly when a blocked script is re-enabled - New:
data-consent-expiryattribute — configure re-prompt interval (default 365 days) - Improved: GTM dataLayer intercept now handles dynamically injected GTM containers
- Improved: Shadow DOM banner now correctly renders in all major browsers including Safari 17+
- Fixed: GPC signal detection was not firing on Firefox 124+ — resolved
- Fixed: Banner z-index conflict with Intercom widget on mobile — resolved
v1.1.0 — March 2026
- New: CCPA 2026 dark pattern enforcement — asymmetric button configurations rejected at publish time
- New:
window.ConsentPixel.getConsentLog()— retrieve the current visitor's consent log entry - New: 12-language support — auto-detection from
Accept-Languageheader - Improved: Pixel bundle size reduced from 9.2kb to 7.8kb gzipped
- Improved: Consent Mode v2 signals now fired synchronously before any dataLayer.push
v1.0.0 — January 2026
- Initial release — MutationObserver script blocker, shadow DOM banner, Cloudflare edge delivery
- Google Consent Mode v2 — all four signals
- GDPR, CCPA, CIPA regulation auto-detection
- Immutable consent log POST to Supabase Edge Functions
- GPC signal support (navigator.globalPrivacyControl + Sec-GPC header)