Handling GDPR and CCPA in AdSense Without Breaking Stuff
What Google Actually Wants in Your Privacy Policy
There’s a checkbox in AdSense that says something like “I confirm my site complies with GDPR/CCPA.” Ticking it is easy. Making it true is the part no one tells you how to actually do. Google provides hand-wavey guidance, but they’ve delegated the dirty work: informing users, offering opt-outs, and keeping that behavior compliant is on you.
If you’re monetizing content in the EU or California (even indirectly through a third-party script), you’re on the hook. Their crawler doesn’t care if you’re a blog or a billion-dollar SaaS: if they flag your site as lacking a compliant disclosure, you could see ads disabled with no warning. I’ve had it happen—mid-month, RPM dropped to zero, and every placement went blank. The privacy policy URL broke due to a redirect loop, and their system killed serving instantly.
Minimum bar: you need to explain what data is collected (cookies, IP, location), who’s collecting it (you, AdSense, others), and how users can opt out. Cookie Consent popups are expected—not optional. You can’t skip them hoping no one notices.
GDPR vs. CCPA: Similar Words, Very Different Teeth
A lot of people (reasonably) slap on some generic privacy boilerplate and hope it passes the smell test, but GDPR and CCPA don’t care how tired you are.
- GDPR (EU) requires prior, active consent for tracking/processing personal data. No consent = no cookies.
- CCPA (California) allows tracking, but users must be able to opt out—especially out of “sale” of personal data.
- Under GDPR, you can’t use AdSense without a cookie banner that lets users say “no.” You need documented consent before dropping tracking scripts.
- Under CCPA, you don’t need opt-IN, but you *do* need a prominent DO NOT SELL MY INFO link.
The logic sounds harmonizable—but implementation is a minefield. The services that offer unified GDPR+CCPA compliance? Half of them misclassify AdSense tags or miss the dynamic loading chains completely.
Cookie Banners: Yes, You Need a Real One
I tried getting by without a third-party consent platform. Built a barebones cookie consent div, referenced categories in the JS logic (“analytics”, “advertising”), and tied it into a toggle. Looked okay. Didn’t actually stop AdSense from setting cookies. Because the first load preemptively injects ad scripts from /pagead/js/* before your banner even renders. You need to change how and when the ad code is injected based on consent status.
The fix is not pretty. Here’s what I ended up doing:
if (userGaveConsent) {
var script = document.createElement('script');
script.src = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
script.setAttribute('data-ad-client', 'ca-pub-XXXX');
script.async = true;
document.head.appendChild(script);
}
You have to defer loading AdSense until after you’ve captured consent. That breaks default behavior if you’re relying on AdSense’s auto ads. Unless you’re using GPT (Google Publisher Tags) to manage async loading, you’ll need to coordinate script invocations yourself.
What Happens When Your Privacy Policy URL Breaks
One morning I woke up to an AdSense warning: “We couldn’t access your privacy policy at [URL].” I’d migrated the site to Cloudflare Pages and forgot to update the privacy policy slug in the dashboard. Problem: their crawler uses an old-school, non-JS bot that bombs out on single-page apps. Anything client-side routed? 404.
No user ever noticed, but AdSense did—which led to reduced ad delivery and, temporarily, no EU traffic monetized. Had to drop in a raw HTML fallback policy at /privacy-policy.html and hardwire it into the robots.txt sitemap.
If your site uses JS routing (Next.js, React, Angular), your privacy policy MUST be available at a canonical, static path with no hashbangs, query params, or JS-based redirections. They don’t follow them.
Third-Party Scripts That Break Consent Logic
One of the more maddening bugs: CMP tool says consent required, banner loads, user declines—all good—and then you realize some random social share button (looking at you, AddThis) is still dropping cookies. Because the widget calls back to adtech partners when rendered. Not even clicked—rendered.
You end up playing whack-a-mole:
- Anything loading iframes before consent? Block it.
- Any React component calling document.createElement on mount? Delay it.
- Lazy loading SDKs? Still counts if they run before consent.
Most AdSense partners also include Google Marketing Platform tags (DoubleClick, etc). Cookiebot flags some, misses others. Tag Manager isn’t much better—unless you’re manually gating firing rules based on the consent model you patched together with sticky notes and regret.
CCPA Opt-Outs Need URL-Level Exposure
CCPA compliance isn’t just about showing a banner. You need a clear URL where users can opt out of data sale. That URL has to work whether JS loads or not. That means:
- Create a dedicated page like /do-not-sell-my-info
- Link it in your site footer
- Have it explain what “personal information” you collect (IP, cookies, device IDs)
- Give a form or mechanism for opting out (email, preferences, existing cookie tools)
Even if you don’t “sell” data in the classic sense, the CCPA defines ad targeting via third parties as potentially qualifying. If you’re passing IP/UA data to Google Analytics plus AdSense/Ads, some lawyers would argue that counts.
Google actually doesn’t tell you whether you’re compliant—they just say you must be. If you get flagged during enforcement (by platform or regulator), it’s a little late to clarify your intentions.
Rewriting Auto Ads Behavior After Consent
AdSense Auto Ads, for all their hands-off utility, do not wait for your consent system. They boot as soon as the base script loads. This means that as soon as a user loads your page, Google’s servers are informed—regardless of your user’s current preference. That’s not GDPR compliant.
So to fix it, you need to:
- Prevent the initial auto ad tag from even rendering without consent
- Upon consent, dynamically load the pagead script manually
- Trigger Auto Ads start logic (yes, this is buried in forum threads)
The trick is this: after loading the script manually, use this little-known method:
(adsbygoogle = window.adsbygoogle || []).push({});
That will initiate the ad slot parsing. But none of this is documented publicly in terms of GDPR-aware behavior. Copied that from a script dump after reverse engineering how Google loads it on News sites. Their support docs act like this isn’t your problem, which… yeah.
Cookie Storage Scope Keeps Screwing You Over
It took me 90 minutes once to realize my privacy banner remembered the consent preference—just not on page refresh. Turns out if you use localStorage on a www. subdomain, but load ads via non-www (or vice versa), cookies aren’t shared. AdSense operates off google.com
or googlesyndication.com
, so if you don’t specify cookie scope/path/duration correctly, the dismiss state never gets recognized.
Also, mobile Safari caps localStorage aggressively when low on memory. Your consent flag might persist on desktop, then evaporate on iOS under memory pressure. Not theoretical—happened during a client audit with 50% CMP failure rate.
If you’re using cookies to manage banner state, add these:
- Set SameSite to Lax or None depending on iframe use
- Specify the domain as exactly your full hostname
- Use secure=true if running HTTPS (which, come on, you are… I hope)
Verification Tools Are Barely Trustworthy
Most “Is your site compliant” scanners give you a surface-level check. They detect the presence of a banner and a privacy policy link, but don’t test actual behavior. I once failed a GDPR scan on a site that WAS compliant—turns out the scanner followed a redirect, got back a 200, but never parsed the page’s dynamic content. Their logic didn’t support JS-drawn DOM updates.
Better bet: use your browser DevTools, set “disable cache,” simulate slow network, and actually walk the cookie setting before/after consent. Watch Application > Cookies
and Network > Initiator
column. That’s the only way I trust you’re doing it right anymore.
Also: some CMPs like OneTrust or CookieYes say they handle blocking, but only do so on first-load. If consent is toggled mid-session, their JS doesn’t retroactively delete already-fired trackers unless you wire up listeners manually.
GDPR Logs and Audit Trails Aren’t Optional
One thing that caught me off guard: under GDPR, if a complaint is filed, you need to show that the user consented—at that moment. That means logging browser sessions, timestamps, and consent status. You’re legally required to be able to demonstrate it. Most CMPs offer this feature… behind a paywall.
I eventually rolled a consent logger into my server logs, using this pattern:
user_id: 19ef-ad33
consent: granted
platform: desktop_chrome
ip: 88.99.xx.xx
timestamp: 2024-04-14T14:06:51Z
I anonymize it lightly, but at least it gives me a defensible timeline if ever questioned. And yes, I’ve had to submit this to an ad partner when their compliance team drilled me over a third-party request analysis.
None of this is in Google’s AdSense docs. Because it’s your problem, not theirs.