← All work

Case study · GA4 · Measurement Protocol · Reliability

GA4 saw a third
of the leads.

The leads were real: they were in the sheet, the team had answered them. GA4 just didn't know about most of them, and neither did the ad platforms learning from GA4.

Client
Innovation Campus, coworking in Málaga
Role
Growth engineer (freelance)
Tools
GA4 Measurement Protocol, Cloudflare Workers, D1, Vitest, Claude Code
When
Sep – Oct 2026
37%
of real leads GA4 recorded before
3
different ways a hit was dying
100%
of office and terrace leads matched after the fix

Summary

Problem

Over four weeks GA4 recorded 37% of the leads in the lead sheet, the actual record.

Solution

Hits that survive the page closing, events sent from the server where a lead is certain, and a log line for every lead saying whether its event was sent, skipped or failed.

How I found it

I stopped reading GA4 on its own and compared it, day by day, with the lead sheet and the database copy of it. That one comparison found every problem below. Each fix was then re-checked the same way.

Three ways a hit died

  • The redirect won the race. The event fired just before the browser moved to the thank-you page, and the browser could cancel it. Fix: send it as a beacon, which survives navigation.
  • The phone killed the request. The event only fired after the server's answer came back. On mobile, a backgrounded tab or a dropped connection meant the answer never arrived, so the event never fired, even though the lead was saved. Fix: keepalive on the request.
  • The browser never sent it. Ad blockers and Safari's tracking protection drop analytics hits outright. No client-side fix exists. Fix: send lead events from the server, the one place a lead is certain, through GA4's Measurement Protocol, still respecting the visitor's consent.

Making the next gap explainable

Two weeks later, two real leads had no event. Probably visitors who declined analytics, which the code skips by design, but I couldn't prove it from the data. So every lead now writes one log line with its ID: sent, skipped (no consent) or failed (status). The next missing event has an answer in the logs instead of an investigation.

Since then GA4 and the lead record match: 100% of office and terrace leads, 86% of conference leads. The one gap is most likely a visitor without consent.

What I'd do next

Asked “what if the sheet write and the analytics event both fail after the customer already saw success?”, I wrote down two real gaps instead of claiming the pipeline was solid: network failures on the sheet webhook were swallowed silently, and replaying a lead would create a duplicate row. The fix I'd build: write each lead first to a store the Worker controls, with a flag per destination, and let a retry job resend only what failed, with every destination checking the lead ID before writing.

Why not just trust the analytics tool?

Because ad platforms bid on what GA4 tells them. Missing two leads in three means bidding against a business that looks worse than it is.

“If the numbers disagree, count by hand before you trust either.”