# Introduction (/docs) import { Rocket, LayoutDashboard, Code, ShieldCheck } from "lucide-react" import { REGULATION_COUNT_DISPLAY, JURISDICTION_COUNT_DISPLAY, US_STATES_COUNT, SDK_GZIP_DISPLAY } from "@/lib/headline-numbers" Privacy regulations like GDPR, CCPA, and dozens of others now require websites to collect informed consent before running tracking scripts. Getting this wrong means legal exposure, broken user trust, and fines that scale with your traffic. But most consent tools are expensive, bloated, and built around dark patterns: pre-checked boxes, hidden reject buttons, confusing language. They solve the wrong problem: checking a compliance box instead of genuinely respecting your visitors. ConsentStack is a modern consent management platform that handles the full lifecycle: displaying a consent banner, blocking scripts until consent is given, logging every decision for audit trails, and automatically adapting to the right regulations based on where your visitors are. Choose your path [#choose-your-path] } title="Getting Started" description="Add ConsentStack to your site in under five minutes. A quick setup guide for everyone." href="/docs/getting-started" /> } title="Dashboard" description="Configure your banner, manage sites, review consent activity, and control script categories, no code required." href="/docs/dashboard/overview" /> } title="Developers" description="Integrate the SDK, customize behavior, and connect platforms like Google Consent Mode." href="/docs/developers" /> } title="Compliance" description={`GDPR across 32 countries, ${US_STATES_COUNT} US state laws, Brazil, Canada, Japan, India, Singapore, Australia, and federal regulations like HIPAA and COPPA.`} href="/docs/compliance" /> Key capabilities [#key-capabilities] | Capability | Why it matters | | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Automatic geo-detection** | Detects visitor location at the network edge and applies the correct consent model, no manual rules needed. | | **Script blocking** | Third-party scripts are blocked before they execute, so nothing runs without consent. | | **{REGULATION_COUNT_DISPLAY} regulations across {JURISDICTION_COUNT_DISPLAY} jurisdictions** | GDPR, {US_STATES_COUNT} US state laws, LGPD, APPI, HIPAA, COPPA, and more, all built in and kept current. | | **Privacy-first audit logs** | Every consent decision is logged with cryptographically hashed visitor IDs (stored as a SHA-256 hash by the consent edge function before persistence; see supabase/functions/consent/index.ts:230-233), no personal data stored, fully audit-ready. | | **{SDK_GZIP_DISPLAY} SDK gzipped (stub + core)** | Executes synchronously at parse time so trackers can be intercepted before they fire. Your visitors' experience stays fast. | # API Reference (/docs/api-reference) ConsentStack exposes three edge-function endpoints that power the consent banner on your site. The SDK calls these automatically. You only need this reference if you are building a custom integration or debugging network requests. **Base URL:** `https://origins.consentstack.io` The SDK fetches its config from `https://config.consentstack.io` first, with `https://origins.consentstack.io/functions/v1/config` as a fallback. Both endpoints serve the same response shape (defined below). See `packages/sdk/src/constants.ts:14-17` for the resolution order. All endpoints return JSON and include CORS headers. Every request that references a site must include a valid `Origin` header matching a domain registered in your dashboard. GET /functions/v1/config [#get-functionsv1config] Returns the published consent configuration for a site, resolved for the visitor's region and language. ``` GET https://origins.consentstack.io/functions/v1/config?site_key=YOUR_SITE_KEY ``` Query parameters [#query-parameters] | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ------------------------------------------------------------------------------------------ | | `site_key` | string | Yes | Your public site key (found in the dashboard under Site Settings). | | `region` | string | No | Override automatic geo-detection with a specific region ID (e.g. `gdpr`, `us_state_laws`). | | `lang` | string | No | Override automatic language detection with a language code (e.g. `de`, `fr`). | Request headers [#request-headers] | Header | Required | Description | | -------- | -------- | -------------------------------------------------------------------- | | `Origin` | Yes | The requesting domain. Must match a domain registered for this site. | Response (200) [#response-200] ```json { "config": { ... }, "sdkCategories": [ { "id": "essential", "name": "Essential", "description": "Required for the website to function properly.", "consentModel": "exempt" }, { "id": "analytics", "name": "Analytics", "description": "Help us understand how visitors interact with our website.", "consentModel": "opt_in" } ], "resolvedRegion": "gdpr", "resolvedLanguage": "en", "resolvedContent": { "title": "We value your privacy", "description": "...", "acceptAllText": "Accept All", "rejectAllText": "Reject All", "customizeText": "Customize", "savePreferencesText": "Save Preferences", "closeText": "Close" }, "resolvedUi": { "required": "Required" }, "showBranding": true, "version": 3, "site_name": "My Website", "scriptRules": [ { "pattern": "google-analytics.com", "category": "analytics" } ] } ``` Response fields [#response-fields] | Field | Type | Description | | ------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `config` | object | The full published config object (appearance, content, categories, regions, consent rules). Falls back to built-in defaults if no config is published. | | `sdkCategories` | array | Consent categories resolved for the visitor's region. Each entry includes `id`, `name`, `description`, and the applicable `consentModel` (`opt_in`, `opt_out`, `notice`, `notice_required`, or `exempt`). | | `resolvedRegion` | string \| null | The region ID matched from the visitor's location (e.g. `gdpr`, `us_state_laws`, `default`). `null` if no region matched. | | `resolvedLanguage` | string | The language code used for content (e.g. `en`, `de`). Determined by the `lang` parameter, the visitor's browser language, or the region's default language. | | `resolvedContent` | object | Banner and preferences UI strings in the resolved language, with consent-model-specific overrides applied. | | `resolvedUi` | object | Translated UI labels (e.g. the "Required" badge text). | | `showBranding` | boolean | Whether to display ConsentStack branding. `true` for free-tier sites. | | `version` | number | The published config version number. `0` if no config has been published. | | `site_name` | string | The site name from your dashboard. | | `scriptRules` | array | Active script blocking rules. Each entry has a `pattern` (domain pattern to match) and a `category` (the consent category that governs it). | Error responses [#error-responses] | Status | Body | Condition | | ------ | -------------------------------------------------- | ---------------------------------------------------------- | | 400 | `{"error": "site_key is required"}` | Missing or invalid `site_key` parameter. | | 403 | `{"error": "Site is paused"}` | The site has been paused in the dashboard. | | 403 | `{"error": "Missing Origin header"}` | No `Origin` header in the request. | | 403 | `{"error": "Origin not registered for this site"}` | The requesting domain is not registered for this site key. | | 404 | `{"error": "Site not found"}` | No site matches the provided `site_key`. | | 500 | `{"error": "Internal server error"}` | Unexpected server error. | Caching [#caching] Responses include `Cache-Control: private, no-store`. Config changes take effect immediately. There is no CDN caching layer between publish and delivery. Usage cap [#usage-cap] Free-tier sites that exceed their monthly active user cap receive a `200` response with `{"disabled": true, "reason": "cap_exceeded"}` instead of the config payload. The SDK gracefully hides the banner when it receives this response. *** POST /functions/v1/consent [#post-functionsv1consent] Records a visitor's consent decision. The SDK calls this endpoint when a visitor interacts with the banner (accept, reject, customize) and on page load for returning visitors. ``` POST https://origins.consentstack.io/functions/v1/consent Content-Type: application/json ``` Request body [#request-body] | Field | Type | Required | Description | | --------------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `site_key` | string | Yes | Your public site key. | | `anon_id` | string | Yes | A client-generated anonymous visitor ID (max 100 chars). Hashed server-side before storage. | | `categories` | object | Yes | A map of category IDs to booleans representing the visitor's consent choices. Example: `{"essential": true, "analytics": false, "marketing": false}`. Max 50 entries. | | `event_type` | string | Yes | One of `initial` (first interaction), `update` (changed preferences), or `impression` (banner was shown). | | `event_action` | string | No | The action taken: `opt_in`, `opt_out`, `partial`, or `acknowledge`. | | `config_version` | number | No | The config version the visitor saw (for audit trail). | | `region` | string | No | The resolved region ID at the time of consent. | | `consent_model` | string | No | The active consent model: `opt_in`, `opt_out`, `notice`, or `notice_required`. | | `regulation` | string | No | The regulation that applied (e.g. `gdpr`, `us_state_laws`). | | `time_to_action_ms` | number | No | Milliseconds between banner display and visitor action. | | `interaction` | string | No | How the visitor interacted (e.g. `accept_all`, `reject_all`, `save_preferences`). Max 50 chars. | | `preferences_opened` | boolean | No | Whether the visitor opened the preferences panel before deciding. | | `banner_load_time_ms` | number | No | Milliseconds the banner took to render. | | `banner_position` | string | No | Banner layout position (e.g. `bottom-full`, `center-modal`). | | `page_url` | string | No | The page URL where consent was given. Max 2000 chars. | | `sdk_version` | string | No | The SDK version that sent the request. | | `language` | string | No | The visitor's browser language. | | `resolved_language` | string | No | The language the banner was displayed in. | Request headers [#request-headers-1] | Header | Required | Description | | -------- | -------- | -------------------------------------------- | | `Origin` | Yes | Must match a registered domain for the site. | Example request [#example-request] ```json { "site_key": "", "anon_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "categories": { "essential": true, "analytics": true, "marketing": false }, "event_type": "initial", "event_action": "partial", "config_version": 3, "region": "gdpr", "consent_model": "opt_in", "time_to_action_ms": 4200, "interaction": "save_preferences", "preferences_opened": true, "page_url": "https://example.com/pricing", "sdk_version": "0.3.6" } ``` Server-side enrichment [#server-side-enrichment] The server adds several fields before storing the consent log: * **Visitor ID hashing.** The `anon_id` is hashed with SHA-256 before storage. The raw ID is never persisted. * **User-Agent parsing.** Browser name, OS, and device type (desktop, mobile, tablet) are extracted from the `User-Agent` header. * **Country detection.** The visitor's country code is read from infrastructure headers at the network edge. * **Config linkage.** The consent log is linked to the published config ID for a complete audit trail. Response codes [#response-codes] | Status | Body | Condition | | ------ | -------------------------------------------------- | ---------------------------------------------------------------------- | | 201 | `{"success": true}` | Consent log recorded successfully. | | 400 | `{"error": "..."}` | Validation error (missing fields, unknown category IDs, invalid JSON). | | 403 | `{"error": "Site is paused"}` | The site is paused. | | 403 | `{"error": "Origin not registered for this site"}` | Domain not registered. | | 404 | `{"error": "Site not found"}` | No site matches the provided `site_key`. | | 405 | `{"error": "Method not allowed"}` | Request used a method other than POST. | | 500 | `{"error": "Internal server error"}` | Unexpected server error. | Category IDs in the `categories` object are validated against your published config. Sending an unknown category ID returns a `400` error with the invalid IDs listed in the message. Impression events skip this validation. *** POST /functions/v1/scripts [#post-functionsv1scripts] Reports third-party scripts detected on the page. The SDK scans the DOM for script elements, extracts their domains, and sends them to this endpoint. The server upserts each script and auto-categorizes known tracker domains. ``` POST https://origins.consentstack.io/functions/v1/scripts Content-Type: application/json ``` Query parameters [#query-parameters-1] | Parameter | Type | Required | Description | | --------- | ------------- | -------- | ------------------------------------------------------------------------- | | `sync_id` | string (UUID) | No | Links this report to a tracker sync session initiated from the dashboard. | Request body [#request-body-1] | Field | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------------------------------------------------------- | | `site_key` | string | Yes | Your public site key. | | `page_url` | string | Yes | The page URL where scripts were detected. Max 500 chars. | | `scripts` | array | Yes | Array of detected scripts (max 100). Each entry contains the fields below. | | `timestamp` | number | Yes | Unix timestamp of the detection. | | `sync_id` | string | No | UUID linking to a dashboard-initiated sync (deprecated in body; use query parameter). | | `error` | string | No | Error message if the scan encountered issues. Max 500 chars. | Script entry fields [#script-entry-fields] | Field | Type | Description | | ---------- | -------------- | -------------------------------------------------------------------------------------- | | `src` | string | The full script `src` URL. Max 2000 chars. | | `domain` | string | The extracted domain of the script (e.g. `google-analytics.com`). Max 500 chars. | | `category` | string \| null | The SDK's best-guess category from its local known-tracker list, or `null` if unknown. | | `blocked` | boolean | Whether the SDK blocked this script from executing. | Example request [#example-request-1] ```json { "site_key": "", "page_url": "https://example.com/", "scripts": [ { "src": "https://www.google-analytics.com/analytics.js", "domain": "google-analytics.com", "category": "analytics", "blocked": true }, { "src": "https://cdn.example.com/widget.js", "domain": "cdn.example.com", "category": null, "blocked": false } ], "timestamp": 1709654400 } ``` Server-side behavior [#server-side-behavior] For each reported script, the server: 1. **Upserts by domain.** If the domain has been seen before for this site, the existing record is updated. New domains are inserted. 2. **Auto-categorizes.** Known tracker domains are matched against a built-in database and assigned a category automatically. 3. **Updates sync status.** If a `sync_id` is provided, the corresponding tracker sync record is marked as complete (or failed, if an `error` was included). Response codes [#response-codes-1] | Status | Body | Condition | | ------ | ------------------------------------ | --------------------------------------------------- | | 200 | `{"success": true}` | Scripts processed successfully. | | 400 | `{"error": "..."}` | Validation error (invalid body or sync\_id format). | | 404 | `{"error": "Site not found"}` | No site matches the provided `site_key`. | | 405 | `{"error": "Method not allowed"}` | Request used a method other than POST. | | 500 | `{"error": "Internal server error"}` | Unexpected server error. | *** Rate limiting [#rate-limiting] All endpoints are rate-limited at the CDN edge: **20 requests per 10 seconds per IP address** (\~120 requests/minute). This limit is for abuse prevention and should not affect normal SDK usage. What's next [#whats-next] * [Getting started](/docs/getting-started). Install the SDK and publish your first config. * [Dashboard guide](/docs/dashboard/overview). Manage sites, configs, and view consent logs. # Changelog (/docs/changelog) import { REGULATION_COUNT_DISPLAY, JURISDICTION_COUNT_DISPLAY } from "@/lib/headline-numbers" All notable changes to ConsentStack are documented here. *** October 2026: Privacy Policy Generator [#october-2026-privacy-policy-generator] * New **Policies** page in the dashboard. Generate a privacy policy from a short business profile and the services detected on your site, with a live preview while you answer. Available on every plan. * Every published version is hosted by ConsentStack at its own address, with an embed code for your own policy page that shows each new version as you publish it. * The banner's policy link now follows your published policy. The Policies page replaces the old Documents page. * [Read the guide](/docs/dashboard/policy-generator), or [see what the generator does](/privacy-policy-generator). August 2026: Theming Override Tokens [#august-2026-theming-override-tokens] * New CSS override tokens for per-role theming: checked and unchecked toggle track, toggle thumb, focus ring, primary button border, and preferences modal size. Each inherits from your existing theme colors until you set it, so existing configurations are unaffected. * Disabled toggles now stay legible on dark themes. March 2026: Initial Release [#march-2026-initial-release] * Consent banner with opt-in, opt-out, notice, notice-required, and exempt models, plus the dynamic resolver for visitor-region-aware behavior * Script blocking for the standard categories (essential, analytics, marketing, functional) * {REGULATION_COUNT_DISPLAY} regulations across {JURISDICTION_COUNT_DISPLAY} jurisdictions * Geo-detection at the network edge * Dashboard for managing sites, configs, and consent logs * JavaScript SDK (\~30 KB gzipped total: stub + core) with synchronous parse-time execution for pre-consent script blocking * React SDK for Next.js and other React frameworks # Compliance Scanner (/docs/compliance/compliance-scanner) import { Step, Steps } from "fumadocs-ui/components/steps" import { Callout } from "@/components/fumadocs/callout" import { Radar, Cookie, ShieldCheck, Activity, Scale, Globe } from "lucide-react" import { REGULATION_COUNT_DISPLAY } from "@/lib/headline-numbers" The compliance scanner is a free tool that checks whether your website respects visitor consent. Enter a domain and, in about a minute, you get a plain report on what your site loads before and after a visitor makes a choice, checked from both the EU and the US. No signup required. You can run it on any site, including your own, as many times as you want. It's a fast way to confirm your consent setup actually works, and to verify a fix after you make one. What it checks [#what-it-checks] } title="Third-party trackers" description="Detects scripts loaded from external domains, including analytics, advertising, and social widgets, and identifies the vendor behind each one." /> } title="Cookie analysis" description="Examines every cookie set during page load, including expiration, security flags, and whether it is first or third party." /> } title="Consent banner detection" description="Identifies which consent platform you use, or flags that there isn't one, and measures how it behaves." /> } title="Pre-consent leakage" description="The most common violation: trackers and cookies that fire before the visitor has made any consent choice." /> } title="Regulation coverage" description={`Evaluates your site against GDPR, CCPA and CPRA, ePrivacy, and ${REGULATION_COUNT_DISPLAY} more applicable regulations.`} /> } title="Geo-variant behavior" description="Scans from both EU and US servers to catch sites that behave differently for visitors in different regions." /> Running a scan [#running-a-scan] Enter your domain [#enter-your-domain] Go to the [compliance scanner](/compliance-scanner) and type in a domain, for example `example.com`. You can paste a full URL too. The scan starts immediately, no account needed. The scanner runs both choices, from both regions [#the-scanner-runs-both-choices-from-both-regions] The scanner visits your site the way a real visitor would, twice. Once it clicks **Reject**, once it clicks **Accept**, and it records what loads each time. It does this from both an EU and a US vantage point, because the rules and the expected behavior differ by region. Read your report [#read-your-report] In about a minute you get a full report: a compliance score, a verdict for each region, and a list of every issue found with what it means and how to fix it. See [Reading your report](#reading-your-report) below. Reading your report [#reading-your-report] Your report opens with a **compliance score** from 0 to 100 and two **regional verdicts**, one for the EU and one for the US. The rules differ by region, so a site can pass in one place and fail in the other. When either region fails, the overall score is capped so a regulator-level failure can never read as passing. Each regional verdict is one of: | Verdict | What it means | | ------------- | ------------------------------------------------------------------------------------------------------------------------ | | Compliant | A working banner is present, Reject blocks non-essential tracking, and nothing fires before a choice is made. | | Non-compliant | Something is wrong: no banner, Reject doesn't work, trackers fire after a Reject, or tracking happens before any choice. | | Not graded | The scan couldn't complete, usually because bot protection blocked it. It is not a pass or a fail. | A non-compliant verdict is not a legal judgment or a fine. It means the scanner observed tracking in a way the law in that region doesn't allow, usually data collection before consent or after a Reject. Treat it as a signal of where to look. Below the verdicts, the report breaks everything out: * **Critical issues** are listed first, in order of severity, each with why it matters and how to fix it. * **Cookies** are listed individually with their domain, party, expiration, and whether they respect the visitor's choice. * **Trackers** are listed with their vendor, category, and whether they fire in the Reject and Accept phases. * **Regulations** show a pass, partial, or fail verdict for each law that applies to your site. Common findings and what they mean [#common-findings-and-what-they-mean] | Finding | What it means | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Tracking before consent | Trackers or cookies fired before the visitor made any choice. Under EU rules, non-essential tracking must wait for opt-in. This is the most common failure. | | Tracking after Reject | Trackers or cookies kept going after the visitor clicked Reject. The visitor's choice is being ignored. | | No banner detected | No consent banner was found on a site that loads tracking which needs one. | | Over-strict | A warning, not a failure. Something is blocked more aggressively than the law requires, which costs you data or analytics without adding compliance. | Reporting a missed banner [#reporting-a-missed-banner] The scanner recognizes a wide range of consent platforms, but it can miss less common or custom ones. If your report says no banner was detected and you know you have one, use the **Report a missed banner** button on the report. Add your email and, if you can, which platform you use. That goes straight to our team and helps us improve detection. What's next [#whats-next] * [Compliance overview](/docs/compliance): how ConsentStack applies the right rules in {REGULATION_COUNT_DISPLAY} regulations worldwide. * [Script blocking](/docs/concepts/script-blocking): how trackers are stopped before they run, which is what a clean scan depends on. * [Testing & verification](/docs/guides/testing-and-verification): confirm your own setup is working as expected. # Global Privacy Control (/docs/compliance/global-privacy-control) What is Global Privacy Control [#what-is-global-privacy-control] Global Privacy Control (GPC) is a browser-level signal that tells websites the user wants to opt out of the sale or sharing of their personal information. It is expressed two ways: * An HTTP request header: `Sec-GPC: 1` * A JavaScript property: `navigator.globalPrivacyControl === true` A site that receives the signal has to treat it as a valid opt-out request. It does not get to ask again, and it does not get to wait for the visitor to click anything. {/* Verified 2026-07-31 against the CPPA approved regulation text (CCPA Updates, Cyber, Risk, ADMT and Insurance Regulations, OAL-approved 2025-09-22, effective 2026-01-01) and the internal regulation catalog audit of 2026-07-21. */} Which US states require GPC [#which-us-states-require-gpc] Twelve US states require businesses to recognize an opt-out preference signal such as GPC: California, Colorado, Connecticut, Delaware, Maryland, Minnesota, Montana, Nebraska, New Hampshire, New Jersey, Oregon and Texas. Two more are already scheduled. Louisiana requires recognition when its Data Privacy Act takes effect on January 1, 2027, and Vermont when the Data Privacy and Online Surveillance Act takes effect on January 1, 2028. Alabama's law takes effect on May 1, 2027 but does not require signal recognition until 2028, and Oklahoma's law, effective January 1, 2027, does not require it at all. The [US state privacy law tracker](/regulations/region/us-state-laws) compares all of these laws side by side, including which require signal recognition and when each takes effect. Outside the US, GPC is arguably binding under GDPR Article 21(5) for processing grounded in legitimate interest. The practical answer for most sites is to honor GPC everywhere rather than sorting visitors by state first. ConsentStack does that by default. Geo-gating the signal costs more engineering than honoring it globally and gives everyone outside those twelve states a worse experience. Browser support [#browser-support] | Browser | Support | Default | | --------------------------- | -------------- | --------------------------------------------------------------------------------------------- | | Brave | Native | On | | DuckDuckGo Browser | Native | On | | Firefox | Native | Off (user opts in at Settings, then Privacy, then Tell websites not to sell or share my data) | | Chrome, Edge, Safari, Opera | Extension only | Requires installing a privacy extension | That last row has an expiry date. California's AB 566, the Opt Me Out Act, was approved by the Governor on October 8, 2025 and becomes operative on January 1, 2027. It says a business "shall not develop or maintain a browser that does not include" a setting that lets the user send an opt-out preference signal. Once Chrome, Safari and Edge ship that setting, GPC stops being a privacy-enthusiast signal and starts arriving from ordinary visitors. How ConsentStack detects GPC [#how-consentstack-detects-gpc] ConsentStack reads GPC from both surfaces at every page load: 1. **Edge detection** at the config-delivery worker. The `Sec-GPC: 1` header is read and included in the config response. 2. **Client detection** in the SDK. `navigator.globalPrivacyControl` is read at initialization. The effective GPC state is the OR of both signals. This belt-and-suspenders approach catches proxies that strip the header and extensions that inject GPC after the config fetch. What ConsentStack does when GPC is detected [#what-consentstack-does-when-gpc-is-detected] Behavior branches on the visitor's jurisdiction. Opt-out jurisdictions (CA, CO, CT, DE, MD, MN, MT, NE, NH, NJ, OR, TX, and other US states) [#opt-out-jurisdictions-ca-co-ct-de-md-mn-mt-ne-nh-nj-or-tx-and-other-us-states] * The consent banner does not show. * A small toast appears at the bottom of the page confirming the opt-out, with a link to manage preferences. * All non-essential cookie categories are set to denied. * Consent Mode v2, Meta LDU, TikTok Limited Data Use, Pinterest Limited Data Sharing, and equivalent downstream signals fire automatically. Opt-in jurisdictions (EU GDPR, UK, EEA) [#opt-in-jurisdictions-eu-gdpr-uk-eea] * The consent banner does show, as it always has in these jurisdictions. Non-essential categories are pre-denied by default, which is the normal opt-in behavior. * If the visitor has previously saved an explicit consent choice on this site, that choice is preserved. GPC sets the default for first-time visitors but does not wipe a prior, explicit decision. This matches CCPA section 7025(c)(3), which lets a business obtain consent where the signal conflicts with a visitor's own site-specific setting, and the W3C GPC spec's stance that explicit user choices override the default signal. All other jurisdictions [#all-other-jurisdictions] ConsentStack defaults to honoring GPC globally. This is configurable per site: in the dashboard under Config, then Settings, then Geo and compliance, the Global Privacy Control setting offers three modes. "Honor everywhere" is the default and our recommendation. "Only where legally required" limits automatic honoring to jurisdictions whose privacy law requires opt-out-signal recognition, tracked against our regulation catalog so new state laws are picked up as they take effect. "Off" disables automatic handling entirely; if you choose it, your team is responsible for honoring the signal another way, and the dashboard shows a warning to that effect. Changes take effect when you publish. How to check if GPC is working on your site [#how-to-check-if-gpc-is-working-on-your-site] Turn GPC on in your browser, open your site in a fresh session, and check three things in order: that your browser is sending the signal, that your site received it, and that trackers actually stopped firing. Most checks stop after the first two. That is how a site can pass a spot check and still be leaking data to advertising vendors. 1. **Turn the signal on.** Brave and the DuckDuckGo browser send GPC by default. In Firefox, go to Settings, then Privacy and Security, then turn on "Tell websites not to sell or share my data". Chrome, Edge, Safari and Opera need a privacy extension for now. 2. **Confirm your browser is sending it.** Open developer tools. In the Console, run `navigator.globalPrivacyControl` and expect `true`. Then open the Network tab, reload, click any request to your own domain, and look for `Sec-GPC: 1` in the request headers. Some setups send one and not the other, which is exactly why ConsentStack reads both and takes either as a yes. 3. **Confirm your site received it.** Reload the page. In a US opt-out state you should see the confirmation toast instead of the cookie banner. In the EU or UK you should still see the banner, with every non-essential category switched off before you touch anything. Opening the preferences panel should show those categories off, not on. 4. **Confirm trackers actually stopped.** This is the step that decides whether you are compliant. With GPC on, reload and do not click anything on the banner. In the Network tab, look for requests to your analytics and advertising vendors. There should be none. If Google Analytics, Meta Pixel or an ad tag fires before you interact with the page, the signal was received and then ignored somewhere downstream. The [free cookie checker](/tools/cookie-checker) runs this same test for you and lists every tracker that fires before consent. 5. **Check what your site publishes.** Load `https://yoursite.com/.well-known/gpc.json` in a browser. It should return `{ "gpc": true, ... }`. Automated compliance scanners look for that file, and its absence is a common reason a site that honors GPC correctly still gets marked down. 6. **Keep the receipt.** Every consent event ConsentStack records carries two audit fields: `gpc_detected`, which shows whether the signal was present on that event, and `signal_source`, which shows whether the decision came from an honored GPC signal or from a banner click. Both are included in the [JSON consent-log export](/docs/dashboard/activity-and-insights), so if a regulator asks whether a specific visitor's signal was honored, you can answer with that visitor's own records rather than a re-test. When a passing site looks like it is failing [#when-a-passing-site-looks-like-it-is-failing] Three things produce a false alarm: * **You tested in a session that already had a saved consent choice.** An explicit decision the visitor made on your site outranks the signal, by design and by regulation. Test in a private window, or clear site data first. * **Your extension injects GPC after the page starts loading.** The header will be missing on the first request even though the JavaScript property is set. ConsentStack still catches this because it reads both surfaces. * **You tested from a jurisdiction your configuration does not treat as opt-out.** The banner showing in the EU with everything pre-denied is correct behavior, not a failure. The 2026 confirmation requirement [#the-2026-confirmation-requirement] Since January 1, 2026, quietly honoring the signal is no longer enough in California. Amended CCPA regulations changed section 7025(c)(6) from "a business **may** display" to "a business **must** display" whether it has processed the visitor's opt-out preference signal, and section 7026(g) changed the same way for opt-out requests generally. The regulations give the example of showing "Opt-Out Preference Signal Honored" on the page and reflecting the opted-out state through a toggle in the visitor's privacy settings. The amendment also swapped "or" for "and" between those two, so both are expected rather than either. ConsentStack ships both halves with no configuration: * The toast reads "We have honored your browser's Global Privacy Control signal and opted you out of the sale or sharing of your personal information." It is announced to screen readers as a status message rather than an alert. * The preferences panel, reachable from that toast, shows every non-essential category switched off. Bulk Accept and Reject buttons are hidden in this state, so the panel reflects a decision that has already been made instead of inviting the visitor to make it again. This is worth checking on whatever CMP you use. A banner that simply stops firing tags on a GPC visit satisfies the old rule and not the current one. What publishers need to do [#what-publishers-need-to-do] Nothing. GPC handling is fully automatic once ConsentStack is installed. Three optional steps improve your compliance posture: 1. Publish /.well-known/gpc.json on your site [#1-publish-well-knowngpcjson-on-your-site] Automated compliance scanners look for this file at the root of your domain. Add a static file at `https://yoursite.com/.well-known/gpc.json`: ```json { "gpc": true, "lastUpdate": "2026-01-01" } ``` 2. Show visitors their signal status [#2-show-visitors-their-signal-status] The SDK exposes the effective GPC status so you can reflect it in your own UI, for example on your privacy policy page: ```js const gpc = window.consentstack.getGpcStatus() // { detected: boolean, honored: boolean } or null before init ``` React sites get the same via `useConsent().gpc`. Three states fall out of it: no signal (`detected` is false), signal honored (`honored` is true, show "Opt-Out Preference Signal Honored"), and signal detected but overridden by the visitor's own explicit choice (`detected` true, `honored` false). See the [JavaScript API reference](/docs/developers/javascript-api#getgpcstatus) for details. 3. Update your privacy policy [#3-update-your-privacy-policy] If you generate your privacy policy with the [Policy Generator](/docs/dashboard/policy-generator), it adds a Global Privacy Control section for you while your banner honors GPC. Otherwise, add a paragraph like this to your privacy policy: > This website honors Global Privacy Control (GPC) signals. When we detect a GPC signal from your browser, we treat it as a valid opt-out of the sale or sharing of your personal information under applicable US state privacy laws, and as an objection to processing based on legitimate interest under the GDPR. We will not sell or share your personal information for the purposes GPC covers while your browser continues to send the signal. You may revisit this choice at any time through our preferences center. Microsoft Clarity users: one additional step [#microsoft-clarity-users-one-additional-step] If you use Microsoft Clarity alongside ConsentStack, configure your Clarity project dashboard with "cookies off by default." ConsentStack will call `clarity("consentv2", true)` only when the user grants consent. This matches Microsoft's recommended integration pattern and ensures Clarity does not fire cookie-based tracking pre-consent. FAQ [#faq] Does my banner still show if the visitor has GPC enabled [#does-my-banner-still-show-if-the-visitor-has-gpc-enabled] In EU and other opt-in jurisdictions, yes. The banner is still required to collect affirmative consent for consent-based processing. Non-essential categories are pre-denied. In US opt-out jurisdictions, no. A toast appears instead, and all non-essential tracking is denied. What if a visitor previously accepted tracking, then enables GPC later [#what-if-a-visitor-previously-accepted-tracking-then-enables-gpc-later] Their prior explicit consent is preserved. Under CCPA section 7025(c)(3), where the signal conflicts with a visitor's own site-specific setting, a business may obtain the visitor's consent and continue, and that explicit choice persists. New visitors with GPC enabled get default-deny on opt-out categories; they can still grant per-category exceptions through the preferences panel, and that exception survives reloads. To reset their state, the visitor clears site data or revokes individual categories from the preferences UI. Do I need to change my Google Ads or Meta Pixel setup [#do-i-need-to-change-my-google-ads-or-meta-pixel-setup] No. ConsentStack automatically sets Google Consent Mode v2 to denied and applies Meta Limited Data Use when GPC is honored. No changes to your pixel code are required. Do I need to do anything to satisfy the 2026 confirmation requirement [#do-i-need-to-do-anything-to-satisfy-the-2026-confirmation-requirement] No. The opt-out confirmation toast and the preferences panel state are on by default for every site running ConsentStack, so the display obligation under CCPA sections 7025(c)(6) and 7026(g) is met without configuration. What percentage of my traffic has GPC enabled [#what-percentage-of-my-traffic-has-gpc-enabled] Today, typically 2 to 8 percent for consumer sites, and higher for tech-niche audiences. Most of it comes from Brave and DuckDuckGo Browser, where the signal is on by default, plus privacy-extension users on Chrome and Firefox. Treat that as a floor rather than a stable number. California's AB 566 requires browser developers to include an opt-out preference signal setting from January 1, 2027, and the share of GPC traffic will move once Chrome, Safari and Edge ship it. Sites that handle the signal correctly now will not need to do anything when that happens. # Compliance (/docs/compliance) import { REGULATION_COUNT_DISPLAY, JURISDICTION_COUNT_DISPLAY, US_STATES_COUNT } from "@/lib/headline-numbers" ConsentStack comes with {REGULATION_COUNT_DISPLAY} privacy regulations built in, covering {JURISDICTION_COUNT_DISPLAY} jurisdictions worldwide, from GDPR across all 32 EU/EEA countries to {US_STATES_COUNT} US state privacy laws, plus Brazil, Canada, Japan, India, Singapore, Australia, and federal regulations like HIPAA and COPPA. It detects where your visitors are and automatically applies the correct consent rules for their location. GDPR (European Union, EEA, UK & Switzerland) [#gdpr-european-union-eea-uk--switzerland] The General Data Protection Regulation is the world's most well-known privacy law, and one of the strictest. It requires websites to obtain **prior opt-in consent** before running any non-essential tracking, meaning scripts like analytics and marketing pixels stay blocked until a visitor actively says yes. GDPR also requires granular, per-category consent (visitors must be able to accept analytics but reject marketing, for example), a clearly visible reject button, the right to withdraw consent at any time, and documented proof that consent was collected. ConsentStack satisfies all of this automatically. Visitors in GDPR regions see an opt-in banner with per-category toggles, a prominent "Reject All" button, and a re-entry button so they can change their mind later. Every consent decision is logged with a timestamp for your audit trail. This applies to visitors from all 32 covered countries: Austria, Belgium, Bulgaria, Croatia, Cyprus, Czechia, Denmark, Estonia, Finland, France, Germany, Greece, Hungary, Iceland, Ireland, Italy, Latvia, Liechtenstein, Lithuania, Luxembourg, Malta, Netherlands, Norway, Poland, Portugal, Romania, Slovakia, Slovenia, Spain, Sweden, Switzerland, and the United Kingdom. US State Privacy Laws [#us-state-privacy-laws] The US doesn't have a single federal privacy law. Instead, {US_STATES_COUNT} states have enacted their own: 20 are in force, and Oklahoma, Louisiana, Alabama and Vermont take effect in 2027 and 2028. Each has slightly different thresholds and requirements. The key difference from GDPR: most US state laws use an **opt-out** model rather than opt-in. Visitors can use your site normally, but must have a clear way to opt out of data sales, targeted advertising, and profiling. California is the notable exception: CPRA requires opt-in consent for sensitive personal information, layering an extra step on top of the standard opt-out flow. ConsentStack covers all {US_STATES_COUNT} states, and applies the four newest before their effective dates: | State | Law | Consent Model | Effective Date | | ------------- | ------- | --------------------------------- | -------------- | | California | CPRA | Opt-out (opt-in for sensitive PI) | Jan 2023 | | Virginia | VCDPA | Opt-out | Jan 2023 | | Connecticut | CTDPA | Opt-out | Jul 2023 | | Colorado | CPA | Opt-out | Jul 2023 | | Utah | UCPA | Opt-out | Dec 2023 | | Florida | FDBR | Opt-out | Jul 2024 | | Texas | TDPSA | Opt-out | Jul 2024 | | Oregon | OCPA | Opt-out | Jul 2024 | | Montana | MCDPA | Opt-out | Oct 2024 | | Delaware | DPDPA | Opt-out | Jan 2025 | | Iowa | ICDPA | Opt-out | Jan 2025 | | Nebraska | NDPA | Opt-out | Jan 2025 | | New Jersey | NJDPA | Opt-out | Jan 2025 | | Minnesota | MNCDPA | Opt-out | Jul 2025 | | Tennessee | TIPA | Opt-out | Jul 2025 | | Maryland | MODPA | Opt-out | Oct 2025 | | Kentucky | KCDPA | Opt-out | Jan 2026 | | Rhode Island | RIDTPPA | Opt-out | Jan 2026 | | Indiana | INCDPA | Opt-out | Jan 2026 | | New Hampshire | NHPA | Opt-out | Jan 2025 | | Oklahoma | OKCDPA | Opt-out | Jan 2027 | | Louisiana | LDPA | Opt-out | Jan 2027 | | Alabama | APDPA | Opt-out | May 2027 | | Vermont | VDPOSA | Opt-out | Jan 2028 | When a visitor arrives from a covered US state, the banner automatically adapts to the correct consent model. You don't need to track which states have passed laws or what each one requires. ConsentStack stays current and applies the right rules. Other Major Regulations [#other-major-regulations] Beyond GDPR and US state laws, ConsentStack includes regulations from around the world: | Regulation | Jurisdiction | Consent Model | | ---------- | --------------------- | ------------- | | LGPD | Brazil | Opt-in | | PIPEDA | Canada (excl. Quebec) | Opt-out | | Law 25 | Quebec, Canada | Opt-in | | APPI | Japan | Opt-out | | DPDPA | India | Opt-in | | PDPA | Singapore | Opt-out | | APP | Australia | Opt-out | | UK DPA | United Kingdom | Opt-in | {/* AUDIT: PIPEDA scope claim flagged for legal review on 2026-04-28 */} PIPEDA covers 12 Canadian provinces and territories, while Quebec's Law 25 applies separately with stricter opt-in requirements aligned with GDPR. US Federal Regulations [#us-federal-regulations] ConsentStack also includes four US federal regulations that apply based on industry rather than geography: | Regulation | Sector | Consent Model | | ---------- | ----------------------------- | ------------- | | HIPAA | Healthcare | Opt-in | | COPPA | Children's privacy (under 13) | Opt-in | | GLBA | Financial services | Opt-out | | FISMA | Federal agencies | Opt-in | These are available in the regulation library for organizations in regulated industries who need to layer industry requirements on top of geographic consent rules. Default Region [#default-region] Not every country has a specific privacy law. For visitors from countries without one, ConsentStack shows a **notice** banner. This informs visitors about your use of cookies and tracking without requiring them to take action. It's a respectful, transparent approach that keeps your site compliant by default. Geo-Detection [#geo-detection] ConsentStack detects visitor location at the network edge using IP-based geolocation. No GPS, no third-party tracking services, just fast, private location detection that happens before the page loads. If you prefer to show the same experience to all visitors regardless of location, you can disable geo-detection in your config. When disabled, every visitor sees your default region's consent model. Proof of Consent [#proof-of-consent] Privacy regulations don't just require you to collect consent. They require you to **prove** you collected it. If a regulator or auditor asks, you need records showing what each visitor consented to, when, and what version of your consent configuration was active at the time. ConsentStack logs every consent decision automatically. Each log entry includes the visitor's anonymized ID (cryptographically hashed, no personal data stored), their consent choices per category, the timestamp, and the config version. You can review consent activity in the [Activity & Insights](/docs/dashboard/activity-and-insights) section of your dashboard. Data retention varies by plan. Check your plan details for specifics. What's Next [#whats-next] * [Getting started](/docs/getting-started): add ConsentStack to your site in under five minutes * [Banner & Preferences](/docs/concepts/banner-and-preferences): customize what visitors see * [Dashboard overview](/docs/dashboard/overview): manage sites, review logs, and configure consent # Banner & Preferences (/docs/concepts/banner-and-preferences) Your consent banner is often the first interaction a visitor has with your brand. ConsentStack gives you full control over what they see, how they interact with it, and how they can update their choices later. The consent banner [#the-consent-banner] When someone visits your site for the first time, ConsentStack displays a consent banner. This is where visitors make their initial privacy choice. ConsentStack detects each visitor's location and automatically shows the right version of the banner: | Visitor location | What they see | Why | | ------------------------------- | --------------------------------------------- | --------------------------------------------------- | | **EU / UK / EEA** | Accept All, Reject All, and Customize buttons | GDPR requires explicit opt-in consent | | **US states with privacy laws** | Accept All, Reject All, and Customize buttons | State laws like CCPA/CPRA require an opt-out option | | **Everyone else** | A simple "Got it" acknowledgement | No consent collection required, just a notice | You don't need to configure this per region. ConsentStack handles the logic automatically based on your consent rules. Banner layouts [#banner-layouts] Choose from five layout options to match your site's design: * **Bottom bar**: a full-width strip along the bottom of the page. The default, and the most common choice. * **Bottom left / Bottom right**: a compact card in the corner. Less intrusive, good for content-heavy sites. * **Bottom center**: a floating card centered at the bottom. A balanced middle ground. * **Center modal**: a dialog in the middle of the screen with a dimmed background overlay. Impossible to miss. All layouts are fully responsive. On smaller screens, they adapt automatically so buttons remain tappable and text stays readable. The preferences center [#the-preferences-center] When a visitor clicks "Customize" on the banner, the preferences center opens. This is where they get granular control over which cookie categories to allow. Each consent category is listed with its name, a plain-language description, and a toggle. Essential cookies are always on and clearly marked as required, so visitors can see them but can't disable them. This transparency builds trust. The preferences center has its own layout options: * **Modal**: a centered dialog, the default * **Side panel (left or right)**: slides in from either edge of the screen * **Full page**: takes over the entire viewport for maximum clarity At the top, bulk "Accept All" and "Reject All" buttons give visitors a quick way to set everything at once. A "Save Preferences" button at the bottom confirms their choices. The re-entry button [#the-re-entry-button] After a visitor makes their choice and the banner closes, a small floating button appears in the corner of your site. This is the re-entry button. It lets returning visitors reopen the preferences center anytime to update their choices. You can customize the re-entry button's position (bottom-left or bottom-right), choose from eight built-in icons (shield, cookie, fingerprint, settings, and more), or upload your own. Background color, icon color, and border radius are all configurable to match your brand. Customization overview [#customization-overview] Every visual detail of the banner, preferences center, and re-entry button is configurable from the dashboard, with no code required: * **Colors**: primary, secondary, background, text, borders, and link colors * **Typography**: heading and body fonts, font sizes, line height * **Layout**: banner position, preferences center style, re-entry button placement * **Buttons and toggles**: border radius, padding, sizing * **Banner dimensions**: max width, max height, padding, border width, shadow style * **Content**: title, description, and button text for every consent model. Automatic translations for 25 languages built in (every official EU language plus Japanese), with per-language fine-tuning when you want different wording in a specific language. Changes preview instantly in the Config Builder before you publish. Built for everyone [#built-for-everyone] The banner and preferences center are designed to work for all visitors: * **Accessibility-first design** (semantic ARIA roles, keyboard navigation, prefers-reduced-motion support; full WCAG 2.1 AA audit pending) * **Keyboard navigable**: every action is reachable via Tab and Enter, with Escape to close panels * **Respects motion preferences**: animations are disabled automatically for visitors who prefer reduced motion * **Works on all devices**: responsive layouts that adapt from desktop to mobile without configuration Next steps [#next-steps] Ready to customize your banner? Open the [Config Builder](/docs/dashboard/config-builder) in your dashboard to start adjusting colors, layouts, and content with a live preview. # Categories & Regions (/docs/concepts/categories-and-regions) ConsentStack uses two building blocks to decide how consent works on your site: **categories** (what you collect) and **regions** (where your visitors are). Together, they determine which consent rules apply to every visitor automatically. Categories [#categories] A category is a group of cookies or scripts that share a common purpose. When a visitor makes a consent choice, they accept or reject an entire category at once, not individual cookies. Built-in categories [#built-in-categories] ConsentStack ships with **four built-in categories**: essential, analytics, marketing, and functional. New sites are seeded with three of these (essential, analytics, marketing); functional is available in the schema and can be enabled per-site from the dashboard's Config Builder. | Category | Purpose | Requires consent? | | -------------- | ---------------------------------------------------------------------------- | ----------------- | | **Essential** | Cookies your site needs to function (session cookies, security tokens) | No, always active | | **Analytics** | Help you understand how visitors use your site (page views, traffic sources) | Yes | | **Marketing** | Used to deliver personalized ads and track campaigns | Yes | | **Functional** | Enhanced features like chat widgets and saved preferences | Yes | Essential vs. optional [#essential-vs-optional] Essential categories are always active. Visitors cannot disable them, and they do not appear as toggles in the preferences panel. Every other category is optional, and visitors can accept or reject it, depending on the consent rules for their region. Custom categories [#custom-categories] Need something more specific? You can create custom categories (like "Social Media" or "Personalization") that work exactly like the built-in ones. Add them in the Config Builder's compliance tab. Regions [#regions] A region is a geographic grouping tied to a privacy regulation. ConsentStack detects where each visitor is located and matches them to the right region, no configuration needed on your part. Built-in regions [#built-in-regions] | Region | Covers | Regulation | | ------------------------- | --------------------------------------------------------------------------------------- | --------------------------------- | | **GDPR** | 32 countries: the EU, EEA, UK, and Switzerland | GDPR / UK GDPR | | **US State Privacy Laws** | 24 US states with privacy legislation (California, Virginia, Colorado, Texas, and more) | CCPA/CPRA, VCDPA, CPA, and others | | **Everyone else** | All countries and states not covered above (the default) | No specific regulation | The "Everyone else" region acts as a catch-all. Any visitor who does not fall into a more specific region is handled here. How categories and regions work together [#how-categories-and-regions-work-together] Each category has a **consent model** that can vary by region. The consent model controls what the visitor sees and whether scripts run before consent is given. Here are the defaults: | Category | GDPR | US State Laws | Everyone else | | ------------- | ------ | ------------- | ------------- | | **Essential** | Exempt | Exempt | Exempt | | **Analytics** | Opt-in | Per-state | Notice | | **Marketing** | Opt-in | Per-state | Notice | What each model means for your visitors: * **Exempt**: No consent needed. Scripts run immediately. The category does not appear in the preferences panel. * **Opt-in**: Scripts are blocked until the visitor actively accepts. They see Accept and Reject buttons. * **Per-state**: ConsentStack checks which US state the visitor is in and applies the correct model for that state's law automatically. * **Notice**: An informational banner with an Acknowledge button. Scripts run by default. You do not need to memorize these rules. ConsentStack applies the right model for each visitor based on their location, and your defaults are already compliant out of the box. Customization [#customization] The defaults cover the most common setup, but you have full control: * **Add custom categories** to match how your site uses cookies (e.g., "Social Media", "A/B Testing"). * **Add custom regions** if you need to treat a specific country or group of countries differently. * **Override any rule** in the matrix. For example, you could set Analytics to opt-in everywhere, or set a stricter model for a specific region. All of these changes are made in the [Config Builder's compliance tab](/docs/dashboard/config-builder). What's next [#whats-next] * [Banner & Preferences](/docs/concepts/banner-and-preferences): Learn how the consent banner and preferences panel appear to your visitors. # Importing consent from your previous CMP (/docs/concepts/consent-import) Switching consent platforms should not mean re-prompting every visitor who already made a choice. When consent import is enabled for your site, ConsentStack reads the consent record your previous CMP stored in the visitor's browser and honors it: visitors who accepted stay accepted, visitors who declined stay declined, and nobody sees a second banner for a decision they already made. How it works [#how-it-works] 1. On the first page load after your cutover, the SDK looks for your previous CMP's first-party consent cookie (for Cookiebot, the `CookieConsent` cookie). 2. If it finds a valid, explicit decision that has not expired, it maps the categories to your ConsentStack categories and applies them immediately, before any tracking scripts can run. The banner is not shown. 3. The decision is converted into a native ConsentStack record and logged to your consent log with the original evidence attached (the previous CMP's consent ID, timestamp, and region), so your audit trail stays continuous even after you cancel the old vendor. 4. From the second page load on, the native ConsentStack record is used. The old cookie is left in place and simply expires on its own. What gets imported [#what-gets-imported] | Previous CMP category | ConsentStack category | | --------------------- | --------------------- | | Necessary | Essential | | Preferences | Functional | | Statistics | Analytics | | Marketing | Marketing | Only explicit decisions are imported. Records marked as implied consent, malformed records, and records older than your consent lifetime setting are ignored; those visitors see your banner as normal. Declines are imported faithfully. A visitor who rejected tracking under your previous CMP stays rejected under ConsentStack, with no re-prompt. Supported vendors [#supported-vendors] Consent import works through one connector per previous platform. Each connector reads that platform's own consent cookie and maps it onto your ConsentStack categories. * **Cookiebot.** Built, and running in production today. * **Any other platform.** We build the connector for you, before your cutover. Tell us what you are migrating from and it will be ready for your go-live. There is no roadmap queue to join, and nothing here for your developers to build. A connector is a reader for a cookie format we can go and inspect, which is why we write them on request rather than shipping a fixed list. The one requirement is technical: your previous platform has to store its decision somewhere readable in the visitor's own browser. Nearly every platform does. If yours does not, we will tell you before you switch rather than after. Your historical consent log [#your-historical-consent-log] Consent import covers the decision your visitors are carrying right now. Your historical consent log is a separate thing, and we move that for you too, during onboarding. Pull the export from your old provider before you cancel, because we cannot sign into their dashboard for you and most providers cut access at the end of the billing period. We will tell you exactly what to export and take it from there, so your evidence from before the switch ends up in the same place as everything after it. We are not the only platform that carries consent over, and it is worth knowing what the alternatives commit to before you switch. See [do you lose your visitors' consent when you switch CMPs?](/compare/consent-migration) for what each major CMP does, read from its own documentation. Enabling consent import [#enabling-consent-import] Consent import is enabled per site by our team during onboarding. Contact support with the name of your previous CMP and we will switch it on before your cutover. # Consent Models (/docs/concepts/consent-models) Consent models are the rules that determine what your visitors see in the consent banner and whether scripts run before or after they give consent. The six consent models [#the-six-consent-models] Every cookie category on your site is assigned a consent model. The model controls the visitor's experience and when scripts are allowed to execute. | Model | What visitors see | Scripts run before consent? | When it applies | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Opt-in** | A banner with Accept and Reject buttons. Visitors must actively accept before anything runs. | No | GDPR countries (EU, EEA, UK, Switzerland) | | **Opt-out** | A banner with Accept and Reject buttons. Scripts run immediately, but visitors can choose to reject. | Yes | US states with opt-out frameworks (e.g., California under CCPA/CPRA) | | **Notice** | An informational banner with a "Got it" button. No accept/reject choice. | Yes | Countries with no specific consent regulation | | **Notice (required)** | Banner is shown but visitors cannot dismiss it without acknowledging the notice. Used in jurisdictions where pre-consent disclosure is required but a click-to-acknowledge is sufficient. | Yes (after acknowledgement) | Jurisdictions that mandate visible disclosure but allow acknowledgement instead of opt-in (see `packages/sdk/src/types.ts:162` and runtime usage at `packages/sdk/src/types.ts:266,459`) | | **Exempt** | Nothing: visitors never see this category. It is always active and does not appear in the preferences panel. | Yes (always on) | Essential cookies required for your site to function (e.g., session cookies, security tokens) | | **Dynamic** | Varies, resolved automatically based on the visitor's US state. | Depends on the resolved model | US states with privacy laws, where requirements differ by state and data type | How models are assigned [#how-models-are-assigned] You do not need to figure out which model applies to which visitor. ConsentStack detects each visitor's location automatically and applies the correct consent model based on the privacy laws in their region. Here is what the defaults look like out of the box: | | GDPR countries | US state privacy laws | Everyone else | | ------------- | -------------- | --------------------- | ------------- | | **Essential** | Exempt | Exempt | Exempt | | **Analytics** | Opt-in | Dynamic | Notice | | **Marketing** | Opt-in | Dynamic | Notice | You can customize these rules in the **Compliance** tab of the Config Builder. For example, you might set Analytics to opt-in everywhere, or add a custom category with its own rules per region. Dynamic consent in the US [#dynamic-consent-in-the-us] Privacy laws in the United States are not uniform. Each state has its own rules, and those rules can vary depending on the *type* of data involved. This is why the US region uses the **dynamic** model instead of a single fixed rule. When a visitor arrives from a US state with a privacy law, ConsentStack checks the specific requirements for that state and resolves each cookie category to the appropriate model. The result is that different categories can end up with different models for the same visitor. For example, a visitor from California might see: * **Analytics** resolved to **opt-out** (CCPA/CPRA allows data collection with an opt-out option) * **Marketing** resolved to **opt-out** (with opt-in required for sensitive personal information per CPRA) This all happens automatically. You do not need to configure state-level rules manually. ConsentStack has the requirements for all 24 US states with a privacy law built in. Dynamic is never a visitor-facing model. Your visitors always see one of the concrete models: opt-in, opt-out, or notice. Dynamic is just how ConsentStack knows to check state-level rules before deciding. What's next [#whats-next] * Learn about [cookie categories](/docs/concepts/categories-and-regions) and how to organize the scripts on your site * Set up your consent rules in the [Config Builder](/docs/dashboard/config-builder) # Core Concepts (/docs/concepts) ConsentStack works out three things on every page load: where your visitor is, which consent rules apply there, and which scripts have to stay blocked until they choose. These pages explain the moving parts behind that. If you are setting up for the first time, start with [Getting Started](/docs/getting-started). Come back here when you want to understand why the banner behaved the way it did. Consent Models [#consent-models] Opt-in, opt-out, and notice-only are three different legal defaults, and the right one depends on where your visitor is sitting. This page covers how ConsentStack picks a model per visitor and what each one changes about the banner. Categories & Regions [#categories--regions] Every cookie and script is sorted into a category, and every visitor is matched to a region. Together those two decide what runs before consent. This page covers the category definitions, how regional rules are resolved, and what happens when a visitor's location is unclear. Script Blocking [#script-blocking] The part that does the actual work. ConsentStack detects third-party scripts and holds them until consent allows them, including tags injected by Google Tag Manager. This page explains the blocking mechanism and how to check it is working. Banner & Preferences [#banner--preferences] What your visitors see and interact with: the banner itself, the preferences center, and the re-entry control that lets someone change their mind later. Importing Consent [#importing-consent] Switching from another CMP does not have to reset every visitor. ConsentStack can read the consent record your previous platform left behind and honor it, so returning visitors are not asked again. This page covers which platforms are supported and what carries over. # Script Blocking (/docs/concepts/script-blocking) import { TRACKER_PATTERN_COUNT_DISPLAY, VENDOR_COUNT_DISPLAY } from "@/lib/headline-numbers" ConsentStack finds the third-party scripts running on your site and blocks them until your visitors consent to the relevant category. No manual tagging required for known scripts. It just works. How scripts are detected and categorized [#how-scripts-are-detected-and-categorized] When a visitor loads your page, ConsentStack checks every third-party script in the browser, as it loads, against a built-in database of **{TRACKER_PATTERN_COUNT_DISPLAY} known tracker patterns** from **{VENDOR_COUNT_DISPLAY} vendors**. If a script belongs to a recognized vendor (Google Analytics, Meta Pixel, HubSpot, and hundreds more) it is automatically assigned to the right consent category. Scripts from domains that are not in the database are flagged as **unassigned** in your dashboard so you can review and categorize them yourself. What happens when you add a new tracker? [#what-happens-when-you-add-a-new-tracker] It is checked the first time it loads. ConsentStack does not crawl your site on a schedule and does not carry a list of trackers from a last scan. The check runs in the visitor's browser on every page load, against the {TRACKER_PATTERN_COUNT_DISPLAY} tracker patterns built into the script itself, so there is no rescan to wait for and nothing to republish. A scan-based consent tool works the other way round: it re-crawls your site on a schedule (commonly monthly, with daily scans sold as an add-on) and can only block what its last crawl found, so a tag added between two crawls fires before consent until the next one. Say your marketing team adds a new pixel through Google Tag Manager on a Tuesday afternoon. On the very next page load, whether that visitor is the first or the millionth: * **If the tracker is in the catalog**, it is blocked before it executes, on that first load, until the visitor consents to its category. This covers tags injected at runtime by a tag manager, an A/B testing tool or a chat widget, not only script tags written into your HTML, and it covers the network calls the tracker would make (`fetch`, `XMLHttpRequest`, `sendBeacon`, pixels, iframes), as described further down this page. * **If the tracker is not in the catalog**, it is recorded but not blocked. It appears in your dashboard's tracker inventory as **Unassigned** (uncategorized) the first time any visitor loads it, with the date it was first seen, the date it was last seen and the pages it appeared on. Assigning it a category, or blocking it outright, is one click. Your configuration regenerates automatically and the tracker is blocked before consent on every page load from then on. That second case is the honest boundary, stated in the terms people use when they compare tools: | Approach | When a new tracker is noticed | What happens before it is categorized | | ---------------------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | **Scan-based** (crawl on a schedule, block what the crawl found) | On the next scheduled crawl | It runs. The gap is everything added since the last scan. | | **Runtime catalog** (ConsentStack) | On its first page load | A catalog tracker is blocked before execution. An unknown one is recorded as Unassigned and runs until you categorize it. | | **Default-deny** (block every unknown request) | On its first page load | It is blocked, and so is any legitimate script the tool has not seen, until someone allows it. | ConsentStack is a known-tracker blocklist evaluated at runtime, not a default-deny firewall: a script from a domain the catalog has never seen runs until you categorize it. That default means a new script cannot silently break your checkout, chat or embeds before a person has looked at it. The trade is that the gap for a scan-based tool is "anything added since the last scan", while the gap here is "anything outside a {VENDOR_COUNT_DISPLAY}-vendor catalog", and you see the second gap in your dashboard on the first page load that carries it, not weeks later. Two things help when you want to be strict about it: * **Test mode** runs the same checks in report-only mode, recording what would have been blocked without blocking anything, so you can review the inventory before you enforce. See [testing on a live site without showing the banner](/docs/guides/testing-and-verification#test-on-a-live-site-without-showing-the-banner). * **Load order.** ConsentStack has to be the first script in your ``. A tracker that loads before it can fire before the check exists, whichever consent tool you use. See [SDK installation](/docs/developers/sdk-installation). To see the behaviour for yourself, add a throwaway tag through your tag manager, open the Network panel in a fresh private window, and confirm that nothing leaves the browser before you click anything on the banner. The steps are in [how to verify any consent tool actually blocks before consent](/docs/guides/testing-and-verification#how-do-you-verify-any-consent-tool-actually-blocks-before-consent). Two ways to categorize scripts [#two-ways-to-categorize-scripts] | Method | How it works | When to use it | | ---------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | **Automatic** | ConsentStack recognizes the script's domain and assigns a category based on its known vendor database. | Works out of the box for the vast majority of third-party scripts. No action needed. | | **Manual rules** | You create a custom rule in the dashboard for a specific domain, assigning it to any category you choose. | For proprietary or niche scripts that the vendor database does not cover. | Manual rules always take priority. If you create a custom rule for a domain that is already auto-categorized, your rule wins. Pattern syntax for manual rules [#pattern-syntax-for-manual-rules] A manual rule's pattern is matched against the hostname of each script request. Two forms are supported: | Form | Example | Matches | Does not match | | ------------------ | -------------------- | ------------------------------------- | ----------------------------------- | | **Plain domain** | `zi-scripts.com` | `zi-scripts.com`, `js.zi-scripts.com` | `zi-scripts.com.evil.net` | | **Wildcard (`*`)** | `s3-*.amazonaws.com` | `s3-us-west-2.amazonaws.com` | `bucket.s3-us-west-2.amazonaws.com` | Rules of thumb: * A plain domain covers itself and every subdomain. This is the right form for almost all rules. * `*` matches any run of characters, including dots. Wildcard patterns must describe the whole hostname: `s3-*.amazonaws.com` does not cover `bucket.s3-us-west-2.amazonaws.com`. Add a `*.` variant (`*.s3-*.amazonaws.com`) when you need subdomain coverage too. * When several rules match the same hostname, the longest pattern wins. * Rules assigned to the essential category never block anything; essential is always allowed. * A pattern of just `*` matches every third-party hostname on your site. That gates everything under one category, which is almost never what you want. What blocking looks like in practice [#what-blocking-looks-like-in-practice] Scripts are stopped **before they execute**, not cleaned up after the fact. This is an important distinction: no tracking code runs until your visitor has actively given consent for that category. When a visitor gives consent: * **Blocked scripts activate immediately.** There is no page reload required. Scripts in the consented category start running right away, in their original page order. If a tracker has multiple scripts (for example, a config script and a pageview script), they execute in the correct sequence: the config script loads and finishes before the pageview script starts. When a visitor revokes consent: * **Already-executed scripts cannot be un-run.** A script that has already loaded and sent data cannot be retroactively stopped. ConsentStack shows the visitor a gentle reminder to refresh the page, which clears the scripts from memory and re-applies blocking. The key trust signal: scripts are blocked *before* execution, not cleaned up afterward. Your visitors' data is never sent to a third party without their consent. What does "blocks scripts before consent" actually mean? [#what-does-blocks-scripts-before-consent-actually-mean] It describes three different mechanisms, and they do not protect visitors equally. A consent tool can rewrite script tags so the browser skips them, intercept the network calls a tracker makes, or let everything load and then tell the vendor what the visitor chose. Only the first two stop data leaving the browser. | Mechanism | What it does | What still gets through | | ------------------------ | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | **Tag rewriting** | Rewrites ` ``` How classification works: 1. If `data-cs-category` is set, that category is used (always wins). 2. Otherwise, the tag's `src` (or `href`, or `data-cs-src` for iframes) domain is matched against the catalog. Hundreds of common vendors are auto-classified. 3. If neither resolves a category, the tag stays dormant. Inline scripts have no `src` to classify, so they require an explicit `data-cs-category`. External scripts and stylesheets from known vendors do not. Many widget libraries (Calendly, Intercom, HubSpot, Drift) inject their own styles when their script initializes, so a separate `` for the stylesheet is often redundant. Try removing the `` first; only mark it with `rel="cs-stylesheet"` if the widget visibly breaks without it. Solution: SSR-emitted preloads in Next.js [#solution-ssr-emitted-preloads-in-nextjs] For frameworks that auto-emit preloads, two patterns avoid the leak: * **Inline the bootstrap snippet** with `dangerouslySetInnerHTML`. The inline ` ``` Each document has its own snippet, so copy it from that document's card rather than from this page. The dashboard's copy already has your site key filled in. | Platform | Where to paste it | | ------------- | ----------------------------------- | | Squarespace | A Code block on the document's page | | WordPress | A Custom HTML block | | Webflow | An Embed element | | Anything else | Any place that accepts raw HTML | The embed works whether or not the ConsentStack banner is installed on that page. You only paste it once: every time the document publishes, the embed shows the new version, with no need to paste again. Once the page loads on one of your site's domains, the card shows where the document is live, for example *Live on example.com/privacy, seen 2h ago*. Visitors with JavaScript turned off see a link to the hosted copy instead. Choose a layout [#choose-a-layout] The embed sheet has a **Layout** setting for each document: * **Accordion** folds each detailed entry, such as a kind of information in the Privacy Policy or a service in the Cookie Policy, into a row visitors can expand. * **Flat** shows every entry open, one after another. Changing the layout updates every embed and the hosted page within a few minutes. The Terms of Service, Disclaimer and Accessibility Statement have no expandable rows, so they look the same either way. Use the hosted page [#use-the-hosted-page] Every published document is also hosted at its own address: ``` https://policies.consentstack.io//privacy-policy ``` The other documents end in `cookie-policy`, `terms-of-service`, `disclaimer` and `accessibility-statement`. The embed sheet shows each link, and **View** on a published card opens it. Use it if you don't have a page to paste the embed into. The hosted copy asks search engines not to index it, so it never competes with the page on your own site. Attribution [#attribution] On the Basic plan, the embed code ends with a line crediting ConsentStack, and the hosted page shows the same line. While branding is on, the document carries the credit at its end even if you delete the line from your page. On paid plans you can remove it with the **Remove branding** feature. In the Config Builder's **Settings** tab, turn off **Show ConsentStack branding** and publish. This removes the credit from your banner and all your documents together. If you pasted the embed code before turning branding off, delete the attribution paragraph from your page, or paste a fresh copy of the embed code. Link the policy from your banner [#link-the-policy-from-your-banner] Your banner should link to your Privacy Policy and Cookie Policy. You only need to add each link once: Open the banner text [#open-the-banner-text] In the [Config Builder](/docs/dashboard/config-builder), open the **Content** tab and click into the banner description where the link should go. Insert the policy link [#insert-the-policy-link] Click the link icon in the text toolbar, then the document icon in the link bar that opens, and choose the document. The menu lists the documents you've published. If no text is selected, the document's name becomes the link text. Publish your banner [#publish-your-banner] Publish the Config Builder as usual so the new text goes live. From then on, the link follows your document. It points to the page where your embed is live, or to the hosted page until the embed has been seen on your site. Until your banner links to each published Privacy Policy and Cookie Policy, the Policies page reminds you and opens Content for you. If you keep your own privacy policy instead of generating one, you don't need the Policies page at all. Click the link icon in the text toolbar and type your page's address into the link bar, like any other link. Versions [#versions] Every publish, yours or an automatic update, creates a new version, and every version stays available at its own address. Choose **Versions** in the card menu to see each one with its publish date and why it was published (*First version*, *Edited answers*, *Automatic update* or *Reviewed update*), and open any of them. The hosted address followed by `/versions` lists them all. An older version opens with a notice giving the date it was published and the date it was replaced, so you can show exactly what your document said at any point. Keeping your policies current [#keeping-your-policies-current] ConsentStack checks your published Privacy Policy and Cookie Policy against your detected services once an hour. Facts update on their own. Anything that changes what a policy promises waits for you. What updates on its own [#what-updates-on-its-own] Each of these publishes as a new version marked *Automatic update*: * A new tool in a category the policy already covers, such as a second analytics tool. * Removing a tool that has been gone from your site for 48 hours. A tool that disappears for a day and comes back stays. * Updated provider details and cookie durations, and cookies a tool starts or stops setting. * Changes to your banner's consent categories (adding or removing one, or changing their names, descriptions or order) or its Global Privacy Control setting, once you publish them in the Config Builder. * A link to another of your policies, when that policy is first published, moves to a page on your own site, or is unpublished. * Template updates from ConsentStack that need nothing new from you. These reach every document. Automatic updates never change your answers. What waits for your review [#what-waits-for-your-review] These change what a policy promises, so nothing goes live until you act: * The first tool in a category the policy didn't cover before, such as your first advertising tool. * A tool that ConsentStack's catalog lists as selling or sharing personal information (for example, for targeted advertising). * A template update that needs a new answer from you. * Live detections replacing the compliance scan the policy was first written from. * A business profile that is missing or incomplete. While a change waits, that policy's other automatic updates wait too. Waiting changes appear in a **Needs review** list at the top of the Policies page, and the **Policies** item in the sidebar shows how many there are. Click **Open** to go to the document's editor, where each change is explained at the top: * **Apply** publishes the change. For a template update, answer the new question right there first. * **Leave out of policy** appears for a tool the policy doesn't name yet. It publishes a new version, marked *Reviewed update*, that leaves the tool out, and keeps it out so the same change doesn't come back. Policy emails [#policy-emails] Every Monday, ConsentStack emails your workspace's Owners and Admins a summary of its policies: the automatic updates published in the past week, and every change still waiting for review, with a button to review each site's changes. The weekly email only goes out when something new happened that week. A change that needs your review doesn't wait for Monday. When one comes up, you get an email the next morning listing every change waiting for review. Terms of Service, Disclaimer and Accessibility Statement [#terms-of-service-disclaimer-and-accessibility-statement] The Terms of Service, Disclaimer and Accessibility Statement don't depend on your detected services. They change only when you publish new answers, when a template update reaches them, or, for the Terms of Service, when the link to your Privacy Policy changes. The Accessibility Statement looks at your detected services once, to fill in any accessibility menu when you first open it. Copy answers from another site [#copy-answers-from-another-site] Each site has its own business profile and its own documents, since different websites often collect different information and use different services. To start a site from another site's answers, open the site's **Settings** and click **Copy from another site** on the **Copy policy answers** card. Workspace Owners and Admins can copy between sites in the same workspace. It replaces this site's business profile and each document's draft answers with the other site's. It doesn't copy published versions, so this site's live documents keep their text until they next publish. Check the business profile straight after copying, especially the website addresses, which still belong to the other site. Every publish reads the profile, including automatic updates, so the copied profile reaches this site's live documents at the next one. # Content Security Policy (/docs/developers/content-security-policy) import { Callout } from '@/components/fumadocs/callout'; If your site sends a `Content-Security-Policy` header, you need to allowlist the ConsentStack endpoints. Without it, the browser blocks the SDK's network requests and the banner can't load its config, log consent, or render its logo. The script tag itself usually succeeds, which makes this a quiet failure: the SDK appears installed, but nothing actually works. This page lists the directives the SDK touches, the minimum allowlist for each, and the most common console errors so you can match a symptom to a fix. What the SDK calls [#what-the-sdk-calls] Once the core script runs it makes three HTTPS requests: | Request | Endpoint | Purpose | | ------- | ------------------------------------------------------ | --------------------------------------- | | `GET` | `https://config.consentstack.io` | KV-cached config (primary fast path) | | `GET` | `https://origins.consentstack.io/functions/v1/config` | Origin config (fallback when KV misses) | | `POST` | `https://origins.consentstack.io/functions/v1/consent` | Consent and impression logging | It also fetches the "Powered by" logo from `https://cdn.consentstack.io/assets/` and injects a `