Cloudflare Web Analytics Over GA4 for This Tech Blog
This article may contain affiliate links. Its content is not affected by advertising.
In short
This blog skipped GA4 to avoid taxing Core Web Vitals twice and adding cookie-consent copy — and the Cloudflare Web Analytics beacon it chose went 2 days unshipped with no traffic recorded.
The short version
tech.rebounder.jp does not run Google Analytics 4 (GA4) for this blog’s own traffic analysis. Instead it measures pageviews with Cloudflare Web Analytics alone. The reason was written down from the start: not double-taxing Core Web Vitals alongside AdSense, and not adding more cookie-consent copy.
But “it’s in the design doc” and “it’s implemented” are different facts. It took 2 days from deciding this policy to the tag actually shipping, and traffic during that gap was never recorded.
Why not GA4
The tech stack for this blog was locked in at design time: hosting on Cloudflare Pages (commercial use allowed on the free tier), measurement via Cloudflare Web Analytics (no cookies needed) — both written down explicitly. The same design doc lists what not to use, right next to it: Vercel (Hobby plan disallows commercial use) and Google Analytics.
Three reasons are given for skipping GA4:
- Loading both the AdSense ad script and a GA4 tag double-taxes Core Web Vitals (the metrics for load speed and perceived responsiveness)
- GA4 uses cookies, which means adding more cookie-consent banner copy
- At this blog’s scale, there isn’t enough traffic for fine-grained behavioral analysis to mean much. From a side-project blog run personally, splitting tracking into six separate tags for granular analysis just meant a data provider suppressed low-volume data anyway, making it unreadable in the end
Cloudflare Web Analytics measures without cookies, which lines up with all three points directly.
Hosting was decided by the same logic
Hosting wasn’t just measurement — it was decided by the same “commercial use allowed, stays on the free tier” bar. Vercel’s free (Hobby) tier doesn’t allow commercial use; running an ad-and-affiliate-monetized blog on Vercel would mean upgrading to a paid plan. Cloudflare Pages’ free tier doesn’t restrict commercial use, so that constraint never comes up.
One more thing noticed along the way: a fully static (zero-JS) project like this one doesn’t even need a Cloudflare Pages adapter package. Astro’s config has no adapter import like @astrojs/cloudflare, and defineConfig specifies no adapter. No Cloudflare-specific package appears in package.json‘s dependencies either. The dist folder the build emits is deployed as-is by Cloudflare Pages’ Git integration — nothing more is needed.
Decided it, then lost 2 days of traffic
The measurement policy was written into the design doc as of 2026-08-15. But the Cloudflare Web Analytics beacon tag didn’t actually land in production HTML until 2026-08-17 — a 2-day lag. Traffic to the already-live pages during that gap was never recorded anywhere. There’s no way to recover it after the fact.
The cause was simple: nothing existed to verify that “written in the design doc” and “the tag actually appears in production HTML” are two different facts. We assumed by eye that “it’s written down, so it must be in there.”
After this, we added a check to the build pipeline that mechanically inspects the built dist output for the tag’s presence. From then on, a missing tag fails the check without anyone needing to look.
Why the token is hardcoded, not an env var
The identifying token in the measurement tag’s data-cf-beacon attribute is written directly in source, not passed through an environment variable. This was deliberate, for two reasons:
- This token is public information visible to anyone the moment it’s output into HTML — there’s nothing to keep secret
- Making it an env var introduces a path where measurement silently stops with no error if the build or a scheduled task fails to pass that variable through. Hardcoding it means that failure path simply doesn’t exist
Routing a value through an env var buys you “can be swapped from outside,” but it always comes bundled with “silently breaks if you forget to pass it.” Building that path just to carry public information wasn’t worth it.
Consistency with the ad loader
This blog has AdSense ad slots too, but it’s set up to skip loading the AdSense script entirely while no ad unit ID is configured. The comment explaining why is direct: loading Google’s script while showing no ads at all would plant a cookie on readers for zero revenue, contradicting the very reason Cloudflare Web Analytics was chosen.
In fact, AdSense was applied for on 2026-08-16 and rejected by late August for “low value content.” Until approved, the ad unit ID stays empty, so this logic means the ad script hasn’t loaded at all so far. The same bar is applied to “why we use something” and “why we don’t” — that consistency shows up here.
Wrapping up
Choosing measurement and hosting wasn’t just a feature-comparison exercise. Fixing the bar first — “a monetization-intent personal/small-scale blog, staying on the free tier for commercial use, without piling on extra cookies” — made Cloudflare Web Analytics over GA4, and Cloudflare Pages over Vercel, fall out naturally.
At the same time, there’s always a lag between deciding a policy and that policy actually landing in the implementation. This blog only noticed after losing 2 days of real traffic data. If you’re weighing the same setup, it’s worth building a mechanical check — not an eyeball check — for whether the policy you decided is actually being emitted into production HTML, before you need it.
Frequently asked questions
Q1Which is better, Cloudflare Web Analytics or Google Analytics 4?
Different jobs. GA4 gives detailed event analysis but needs cookies and a consent banner, and taxes Core Web Vitals too. Cloudflare Web Analytics needs no cookies, staying light but limited to pageviews. For a small ad-monetized blog, lightness can matter more than GA4's detail.
Q2What's the difference between Cloudflare Pages and Vercel here?
Whether commercial use is allowed on the free tier. Vercel's free (Hobby) tier disallows commercial use, so a site monetized with ads or affiliate links needs a paid plan. Cloudflare Pages' free tier doesn't restrict commercial use.
Q3How do you catch a missing measurement tag?
Not by eye or by checking the design doc. This blog added a script to its publish-build checks that mechanically inspects the built dist output for the tag, so a missing tag now fails a check without anyone needing to look.
Environment verified
- Astro ^7 (measured on 7.2.2), static output, no adapter
- Design decided 2026-08-15, implementation landed 2026-08-17 (a 2-day lag)
What this article is based on
- Markdown file lines 105-121commit b29f997
- Markdown file lines 136-141commit b29f997
- file lines 1-3commit b29f997
- file lines 26-35commit b29f997
- JSON file lines 59-70commit b29f997
- Astro file lines 186-198commit b29f997
- Astro file lines 212-218commit b29f997
- TypeScript file lines 4-9commit b29f997
- TypeScript file lines 38-46commit b29f997
Every claim in this article comes from the records above. The repositories we operate are private so we cannot link to them, but which file, which lines, and at which commit we read them is recorded for every article. Nothing here is written from guesswork.