DEV Community

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.