Add analytics to your site without a cookie consent banner
I wanted to know if anyone was actually visiting my new side project - but I really didn't want to slap a GDPR cookie-consent banner on a tool that's supposed to be frictionless. The default move is Google Analytics. But GA sets cookies, which (in the EEA/UK and friends) means a consent banner, a CMP, and a pop-up in every new visitor's face. That's a lot of baggage just to answer "did anyone show up?" Turns out there's a much lighter option: Cloudflare Web Analytics. It's free, cookieless, doesn't fingerprint, and doesn't track people across sites - so no consent banner is required. Here's how I added it, and the one gotcha that bit me.
What you get (and what you don't)
Cloudflare Web Analytics gives you:
- Page views, visits, top pages
- Referrers and countries
- Core Web Vitals (LCP / INP / CLS)
What you don't get: per-user journeys, custom events, funnels - the granular stuff GA does. Because there are no cookies and no client-side state, it can't follow a single user around. For "is anyone using this, and where are they coming from?", that's plenty. For deep product analytics, it's not GA. Know which question you're answering.
Setup, option 1: automatic (zero code)
If your site is proxied through Cloudflare, you don't need to touch your code:
- Cloudflare dashboard β Analytics & Logs β Web Analytics
- Add a site, enter your hostname
- Choose Automatic setup
Cloudflare injects the beacon at the edge. Done. Nothing to deploy.
Setup, option 2: the manual beacon
If you want control (or you're on a framework where you'd rather drop it in yourself), use the beacon snippet:
<script defer src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token": "YOUR_TOKEN"}'></script>
In a Next.js app, I load it only in production via next/script:
import Script from "next/script";
const TOKEN = process.env.NEXT_PUBLIC_CF_BEACON_TOKEN;
export default function CloudflareAnalytics() {
if (process.env.NEXT_PUBLIC_APP_ENV !== "production" || !TOKEN) return null;
return (
<Script
strategy="afterInteractive"
src="https://static.cloudflareinsights.com/beacon.min.js"
data-cf-beacon={JSON.stringify({ token: TOKEN })}
/>
);
}
Gating on production keeps localhost out of your numbers.
The gotcha: ContentβSecurityβPolicy
I ship a fairly strict Content-Security-Policy, and the beacon just⦠didn't fire. No error I noticed at first - it was silently blocked. The beacon loads a script from static.cloudflareinsights.com and sends data to cloudflareinsights.com. If you have a CSP, you have to allow both:
script-src ... https://static.cloudflareinsights.com;
connect-src ... https://cloudflareinsights.com;
If your analytics look empty and you have a CSP, this is the first thing to check.
Do you still need a privacy policy?
The consent banner goes away because there are no cookies and no personal profiling. But "no banner" isn't "no disclosure" - I still mention it in the privacy policy, something like:
For usage measurement we use Cloudflare Web Analytics, which is privacy-first: it doesn't use cookies, doesn't fingerprint, and doesn't track you across sites.
Transparent, and no pop-up nagging every visitor.
Wrapping up
I added this to TempTools and it's honestly a relief: I get country-level traffic and Core Web Vitals, users get zero pop-ups, and I didn't have to wire up a consent platform. If your site mostly needs "how many people, from where" - and especially if it's a small tool where a cookie banner feels heavier than the product itself - cookieless analytics is a really nice default. (Bonus: it's also great for confirming that most of my traffic is, so far, still just me. π )
Comments
No comments yet. Start the discussion.