Privacy-Friendly Analytics That Won’t Break Your Revenue Model
Why Not Just Ditch Google Analytics Entirely?
I mean, you probably already asked this five times. Same. But the problem is, most “alternatives” feel like making toast on a space heater — satisfying until you realize you’re still hungry and nothing scales. Still, if your audience’s trust matters, and your AdSense RPM is already a mystery novel with missing chapters, it’s worth it.
You’re not alone ditching GA. GDPR, ePrivacy, CCPA and now, spoiler alert, the TLDR Act (yes that’s a thing) are all nuking the slow, bloated, consent-heavy model GA relies on. GA4 didn’t help with the increasing entropy either. I had three different clients end up with multiple event definitions for the same click event — because GA4’s UI quietly overwrote existing settings when you toggled a new one. It does that. Still.
But here’s the kicker: most privacy-focused alternatives quietly avoid tracking scripts that insert any user identifiers. That means your ad personalization (and associated fill-rate or even AdSense eligibility in the EU) can crater if you’re not careful.
Plausible, for When You Just Want Pageviews (Without a GDPR Fire)
Plausible.io runs entirely client-side without cookies or persistent identifiers, and those events are aggregated for you in an auto-magical 24-hour rolling summary which somehow… just works.
There’s no IP logging (they hash and truncate), no user-agent tracking beyond raw metadata, and it logs exactly one hit per page. It can’t track returning users in any meaningful way — which on the one hand means you’re safe for EU compliance, but on the other hand means you can’t correlate sessions to any behavioral outcome. Trying to map bounce rate to engagement in Plausible is like squinting at a QR code from across the room.
Still, it’s fast. You can self-host. And there’s a script defer-injected with 20 lines of vanilla JS you can actually read like a novel. I once debugged the entire thing in Firefox’s Inspector without touching console. That never happens.
Fathom Analytics and the Mysterious Case of the Unexplained Spike
Fathom feels like it was built by someone who got burned too many times explaining GA hit-scopes to marketing teams. It’s minimal. Real-time. Tracks events optionally, but only through full-page visits.
I’ve used Fathom via their CDN for a couple of newsletter-heavy clients, and their most fascinating edge case is that it’ll miscount UTM values if you don’t strip fragments properly before redirect. I had a set of ampersand-delimited UTM tags hitting internal routing and triggering internal 301 loops, but Fathom logged each as a distinct referrer — inflating the “Direct” traffic block by about 400 overnight.
“Direct” means “we couldn’t figure it out.” Not kidding — it’s in their docs.
Also worth noting: If you’re using Cloudflare Pages and proxying your own analytics domain to mask the Fathom source (e.g., analytics.yoursite.com
), use Origin Rules and not Page Rules, or caching weirdness will nuke your CSP.
Matomo: The Analytics Stack You’ll Love Until You Update
Oh, Matomo. Self-hosted, check. Extremely customizable, good. Looks like a slightly caffeinated GA3 clone, fine. Then you update from version 4.12 to 4.14 and everything melts.
The update issue happened on a container-hosted install using PHP-FPM. If your config.ini.php doesn’t declare memory optimizations explicitly, Matomo resets back to default lowball values during automatic updates. My error was delicious:
Fatal error: Allowed memory size of 134217728 bytes exhausted
I sat on that particular bug at 2am after pushing a hotfix to enable anonymized IP collection, and ended up hacking in a temporary boost via .htaccess. But nobody tells you Matomo won’t persist INI overrides unless you also update the CLI wrapper.
That said — its GDPR tools are impressively deep. You can configure data retention policies down to the minute, anonymize historical IPs, and even segregate tagged sessions by explicit cookie opt-in. But dear god, don’t enable session-based heatmaps unless you’ve dialed up your buffer cache. It chews everything.
GoatCounter: Bless It, But Maybe Not for Revenue-Sensitive Sites
GoatCounter is so minimalist you’ll want to hug it, then realize it can’t track things like bounce mitigation JS triggers.
I had a side project (non-profit, zero cookie bar, newsletter-driven) on GoatCounter for eight months. It was perfect — until we needed to measure which blogpost layout drove more form submissions. Nope. No goals. No funneling.
It logs aggregate stats only. No IP. No user data. No cookies. And here’s the logic flaw: if multiple hits occur in the same second (e.g., preloaded links + opening tab groups), GoatCounter groups them and logs them as a single visit if user-agent + referring domain match. This is undocumented. Took me forever to find because it only affected my devs, who were testing layouts rapidly.
Also — for AdSense, it’s pretty much invisible. I don’t think it helps or hurts, but if you rely on behavioral data to tweak ad placement, you’ll be guessing.
Cloudflare Web Analytics: Works Until You Use AMP
Cloudflare Analytics is the choice you end up with after you’ve yo-yo’d through every other option and realize you just want fast TTFB and page routes. It’s technically free, privacy-focused, and served via Workers. But…
I ran into an undocumented edge where AMP-powered template caches (especially Google AMP CDN) block CF’s beacon entirely. Page loads show up client-side, but CF logs… zero. Tried setting custom script
priorities, tested different user agents — no dice. The beacon never fires. If AMP is part of your workflow, skip this one.
The shocker? You can still get semi-coherent viewer graphs if AMP hits are distributed alongside other normal visits — it auto-estimates unique views using CF’s own DNS logs, not just JS beacons. I had to dig into ray IDs to figure this out. Kind of fascinating, kind of cursed.
Avoiding Analytics Breakage on Consent-Heavy Sites
If you’re doing anything vaguely legal in France or Bavaria, you’re going to end up with a complex cookie consent flow. Here’s the rub: most analytics scripts break when lazy-loaded based on cookie settings — especially if you defer or async them.
The Timing Race No One Talks About
The timing edge case I hit went like this:
- User loads page before cookie banner shows up
- Bounce trigger fires (because time-on-page < 4s)
- User accepts cookies after ~5s
- Analytics loads, but session already ended
Result? The visit doesn’t count.
This happens inside TCF 2.2 scripts that default to async-mode. Fun.
The fix I ended up hacking together was to preload a non-tracking placeholder analytics shim that logged pseudo-session state using sessionStorage
, then pass it to the real analytics engine after consent. Was it janky? Extremely. But it got those undercounts fixed without relying on tracking cookies.
How These Analytics Choices Affect AdSense Behavior
Let’s not pretend AdSense runs in a vacuum. If your analytics stack drops user-level session data or skips behavioral triggers that parallel ad behavior (like scroll-depth or hover-dwell), you’re gonna have weird ad earnings swings you can’t explain.
One time I moved a client from GA to Fathom and suddenly — I kid you not — RPM dropped by around 30%. Even though the ad slots were untouched. Eventually found out that since no behavioral signals were sent back into Google’s ecosystem (no GA tag, no site engagement weight), the ad serving algorithm decided to treat the pages as lower value for user retention. So it shifted to cheaper inventory.
You can’t control this directly, but you can mitigate:
- Use
adsbygoogle.push({ params })
with custom viewport logic - Feed engagement signals via GTM even without GA itself
- Enable first-party cookies on your domain with AdSense auto ads
- Don’t disable all JS metrics if you rely on CPC ads — it affects targeting
- Run a basic dwell-time tracker as a signal booster (can be cookie-less)
It’s annoying. Feels like shadowboxing with the algorithm.