
To set up a cookie banner the right way, block all non-essential cookies until the visitor opts in (for EU traffic) or gives an easy opt-out path (for California traffic), pick a consent management platform like Cookiebot, CookieYes, or the open-source Klaro, and wire Google Consent Mode v2 if you run Google Analytics or Ads. Skip pre-checked boxes, scroll-to-accept, and any layout where Reject is harder to find than Accept.
That is the whole job in one paragraph. The rest of this post is the founder playbook: which laws apply, how to audit what your stack already sets, which tool to pick at which stage, working code, and the dark patterns regulators actually fine for.
This is implementation advice, not legal advice. If you are in health, finance, EU B2C at scale, or processing kids' data, retain a privacy lawyer before you ship.
Three regimes set the rules: GDPR (EU/UK), CCPA/CPRA (California), and the ePrivacy Directive (EU, sits on top of GDPR for cookies specifically).
GDPR requires prior, explicit, granular, freely-given opt-in consent before any non-essential cookie loads. Consent must be as easy to withdraw as to give. Pre-ticked boxes do not count. Silence does not count. Continued browsing does not count.
CCPA/CPRA is opt-out, not opt-in, but California's 2026 update tightened the screws. You need a clearly posted "Do Not Sell or Share My Personal Information" link, you must honor the Global Privacy Control browser signal automatically, and Reject must be as visible as Accept. Closing a banner without clicking Accept no longer counts as consent.
ePrivacy says the banner must show before any non-essential cookie fires. Strictly necessary cookies (auth session, CSRF, load balancer) are allowed without consent. Everything else (analytics, marketing, personalization, A/B test) needs a click first.
The fines are real. GDPR can hit 4% of global annual revenue or EUR 20 million, whichever is larger. The French CNIL alone has fined Google EUR 150 million and Meta EUR 60 million for asymmetric reject paths. CCPA caps at $7,500 per intentional violation, but "per violation" is per consumer, which adds up fast.
Most founders are surprised by how many cookies their app sets before any user interaction. A typical Next.js app with Stripe Checkout, PostHog, Sentry, and Vercel Analytics drops eight to fifteen cookies on first paint. You cannot ship a banner that lies about what you are setting.
Open Chrome DevTools, go to Application, then Cookies, and load your homepage in an incognito window. Document every cookie: name, domain, expiration, purpose, and whether it is first or third party. The free Cookiebot scanner or CookieServe will do this automatically, but eyeballing it once teaches you what your stack actually does.
Then categorize:
__stripe_mid for fraud prevention. These load by default.Write this list down. The CMP setup screen will ask you for it.
There is no universal best CMP. There is a best one for your traffic shape and team. Here is the honest pricing landscape:
| Tool | Type | Price | Best for | Trade-off |
|---|---|---|---|---|
| Cookiebot | SaaS | $11-69/mo | EU-heavy traffic, Google Consent Mode v2 native | Pricier as monthly visitors scale |
| CookieYes | SaaS | $8-29/mo | Small SaaS, fast setup, multi-domain | Branding on free tier |
| Iubenda | SaaS | $5-29/mo | Privacy policy + cookie policy + terms in one bundle | Generic templates need editing |
| Termly | Freemium SaaS | Free-$25/mo | Solo founders pre-revenue | Free tier limits domains and scans |
| OneTrust | Enterprise | Custom (5-figure) | Series B+ or regulated industries | Overkill and slow to deploy |
| Klaro | OSS | Free | Privacy-first teams who self-host | You own updates, audits, and consent storage |
| Vanilla Cookie Consent | OSS | Free | Tiny landing pages with one analytics tag | No consent log, no CMP audit trail |
Pick by stage, not by feature list:
The Google-certified status matters. Google maintains a list of CMPs whose default integration covers Consent Mode v2 correctly. If your CMP is not on that list, you are wiring it manually and risking gaps.
Since March 2024, any site serving Google Ads or Analytics to EU/EEA users must implement Google Consent Mode v2 or those tags stop returning data. EU conversions vanish from GA4. Smart Bidding loses signal. Remarketing audiences shrink to zero.
Consent Mode v2 introduced two parameters on top of the original four: ad_user_data (consent to send user data to Google for ads) and ad_personalization (consent to personalized advertising). All four (analytics_storage, ad_storage, ad_user_data, ad_personalization) default to denied and flip to granted when the user opts in.
The default-denied snippet goes in the <head> before any Google tag:
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied',
'wait_for_update': 500
});
</script>
Then on consent, the CMP fires gtag('consent', 'update', {...}) with the user's choices. Google's tags backfill modeled conversions for the denied users using machine-learned aggregates, which is most of the point of v2.
Most certified CMPs handle this for you. If you self-host with Klaro or write your own banner, you are responsible for emitting the update event correctly and testing it with Google's Tag Assistant.
A founder mistake we see all the time: shipping a hard EU-style opt-in banner to US visitors who do not need it. You hurt conversion for no compliance gain. Or the inverse: shipping a US-style notice to EU visitors and getting fined.
Use IP geolocation to decide which banner variant to show:
Cloudflare exposes the country code as CF-IPCountry for free on every request. Vercel does the same with request.geo.country. Both let you pick the banner variant server-side before the page hydrates, so the right banner shows on first paint with no flash.
If you are non-technical and not sure how this gets wired, the founders who ship this fastest are usually pairing with someone who has done it before. Most CMPs have a one-click region rule, but an engineer needs to pre-block scripts with the right data-cookieconsent or type="text/plain" attributes for the CMP to do its job. Founders who interview a developer when they cannot code tend to skip this kind of integration debt entirely by asking the right questions in the call.
For a tiny landing page with GA4 only, you can ship a working banner in about 40 lines without a CMP. Here is the skeleton:
<!-- 1. Default consent denied, BEFORE any analytics -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied'
});
</script>
<!-- 2. Banner DOM -->
<div id="cookie-banner" hidden>
<p>We use cookies for analytics. You can accept, reject, or customize.</p>
<button id="cc-accept">Accept all</button>
<button id="cc-reject">Reject all</button>
<a href="/privacy">Cookie policy</a>
</div>
<!-- 3. Show + handle choice -->
<script>
const stored = localStorage.getItem('cc-choice');
if (!stored) document.getElementById('cookie-banner').hidden = false;
if (stored === 'granted') loadAnalytics();
function setConsent(state) {
gtag('consent', 'update', {
'ad_storage': state,
'ad_user_data': state,
'ad_personalization': state,
'analytics_storage': state
});
localStorage.setItem('cc-choice', state);
document.getElementById('cookie-banner').hidden = true;
if (state === 'granted') loadAnalytics();
}
function loadAnalytics() {
const s = document.createElement('script');
s.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXX';
s.async = true;
document.head.appendChild(s);
}
document.getElementById('cc-accept').onclick = () => setConsent('granted');
document.getElementById('cc-reject').onclick = () => setConsent('denied');
</script>
This is the minimum that satisfies GDPR for a single-tag site. Real apps need granular per-category toggles, a "withdraw consent" link in the footer, a consent log (so you can prove a given user opted in), GPC detection, and CCPA-specific UI. That is exactly why most teams reach for a CMP after the second analytics tool lands.
These look reasonable. They are illegal, and the EU has the case law to prove it.
The symmetry rule is the easy mental check: if a regulator screenshot would show "Reject" looking secondary to "Accept," you have a problem.
You are not the first founder to realize at month four that your SaaS has been quietly setting the Meta Pixel on EU visitors with no consent. Here is the triage:
For most pre-seed and seed teams, this is a one-afternoon job once someone who has done it before owns the work. The trap is treating it as a "we'll get to it" item until a customer asks about your DPA in a deal.
If you want to hand the implementation off, every engineer on Cadence is AI-native by default (Cursor, Claude Code, Copilot fluency vetted in a voice interview), and a mid-tier engineer at $1,000/week typically ships a compliant banner with Consent Mode v2 and per-region detection in two to three days. You can book a Cadence engineer with a 48-hour free trial and not pay if the implementation does not land.
Try it: pick one CMP from the table above, install it on a staging branch tonight, and run our ship-or-skip audit against your live site to see what cookies fire pre-consent.
<head> before any analytics or pixel tags load.data-cookieconsent="statistics" (or your CMP's equivalent attribute) to every analytics, marketing, and personalization script tag.Sec-GPC: 1, treat it as an opt-out for sale/share automatically without showing the banner choice.If you serve any California traffic and use analytics or ads, you still need a CCPA notice with a Do Not Sell or Share link. Most US founders need a banner the day they install Google Analytics, even with zero EU traffic. Virginia, Colorado, Connecticut, and a growing list of states are converging on the same opt-out model.
If your analytics tool stores cookies or fingerprints users, you still need consent under GDPR. PostHog, Mixpanel, and Plausible's cookie mode all qualify. Plausible's cookieless mode and Fathom are two of the few real exemptions, since they do not set cookies and aggregate before storage.
EU/EEA conversions disappear from your reports because Google will not accept tag fires without consent signals. Your remarketing audiences shrink, Smart Bidding loses signal, and CPA reporting becomes unreliable. The fix is the default-denied snippet plus your CMP's update event.
Yes, if your traffic is low and your stack is simple. Termly's free tier or Klaro OSS handles most early-stage SaaS. Upgrade when you cross 10,000 monthly visitors, add more than two analytics tools, or sign your first EU enterprise customer who asks for a consent log.
For high-risk verticals (health, finance, kids' data, EU B2C at scale, anything regulated), yes, before you launch. For a B2B SaaS landing page with one analytics tool and a Stripe checkout, a Google-certified CMP plus a generated privacy policy is usually enough at pre-seed and seed. The lawyer becomes worth the hourly rate the moment a real EU enterprise customer sends a DPA.