ga4 not showing data

GA4 not showing data? The full checklist

MU By Muhammad Uzair ·Analytics · Last updated

Google Analytics 4 shows nothing. The tag looks installed, the property exists, and the reports are empty. This is one of the most common problems in analytics and one of the most badly explained, because the usual advice is “wait 48 hours” — which is sometimes right and often a way of avoiding the actual diagnosis.

What follows is the full checklist, in the order worth checking, written after debugging exactly this on my own site. Two of the four failure modes below were mine.

What actually happens between a pageview and a GA4 report

1. Page loads your HTML 2. gtag.js loads googletagmanager.com 3. Beacon sent /g/collect → 204 4. Realtime visible in ~30 seconds 5. Reports 24–48 hours later Breaks here if: the tag isn’t in your HTML at all Breaks here if: CSP script-src blocks googletagmanager.com Breaks here if: CSP connect-src blocks the collect endpoint Looks broken if: you check Reports instead of Realtime RANKWISES.COM

First, understand what has to happen

Between someone loading your page and a number appearing in a report, five things happen. Knowing them turns “GA4 is broken” into a specific question:

  1. Your HTML loads, containing the gtag snippet.
  2. gtag.js downloads from googletagmanager.com and defines the gtag() function.
  3. A beacon fires — a request to google-analytics.com/g/collect carrying your measurement ID.
  4. Realtime updates, usually within 30 seconds.
  5. Standard reports process, taking 24 to 48 hours.

Each step can fail independently, and each fails in a way that looks identical from the GA4 interface: an empty report.

Debugging order — stop at the first failure

No data in GA4 CHECK IN THIS ORDER 1. Is the tag in the live HTML? Console: document.documentElement.innerHTML.includes('G-XXXX') 2. Any CSP errors? F12 → Console → look for “Content Security Policy” 3. Does the beacon fire, and to where? Network → filter “collect” → check tid= matches your ID 4. Check Realtime, not Reports Realtime is seconds. Standard reports take 24–48 hours.

Step 1: Is the tag actually on the page?

Obvious, and still the most common cause. Open your live site, press F12, go to the Console tab and paste this:

document.documentElement.innerHTML.includes('G-XXXXXXXXXX')

Replace the ID with your own. true means the tag is present in the rendered page.

Why not just use View Source? Ctrl+U often serves a cached copy, so it can show an old version of your HTML while the live page is fine. The Console command reads the actual rendered DOM. If the two disagree, trust the Console.

If it returns false, nothing else in this article matters — deploy the tag first. Check every page template too, not just the homepage. On my site the tag was missing from all 49 pages, because I had added the property in GA4 and never added the snippet to the HTML. Creating a property does not install anything.

Step 2: Is a Content Security Policy blocking it?

This one is invisible unless you look for it, and it is what caught me.

A Content Security Policy is a security header that tells the browser which domains are allowed to load scripts, send data, or fetch images. It is genuinely worth having. It will also silently break analytics if the policy does not name Google’s domains.

Open F12 → Console and look for red errors mentioning “Content Security Policy”. GA4 needs two directives:

DirectiveMust allowWhy
script-srcwww.googletagmanager.comto download gtag.js
connect-src*.google-analytics.comto send the beacon
The subtle one: regional endpoints GA4 does not always send to www.google-analytics.com. Depending on the visitor’s location it may use region1.google-analytics.com or another regional host. If your policy names only one, visitors from other regions are dropped silently while your own visits work fine — which is a genuinely confusing failure. Use the wildcard https://*.google-analytics.com.

My policy allowed region1 only. Worse, I had also blocked scripts.clarity.ms, which meant Microsoft Clarity had been installed but collecting nothing for weeks without a single visible symptom.

Step 3: Does the beacon fire, and where does it go?

This is the decisive test. F12 → Network tab → type collect in the filter → reload the page.

What you seeWhat it means
A request returning 204Data reached Google. 204 means “accepted, no content to return” — the correct response.
Request blocked or failedCSP or an ad blocker. The Console will name which.
No request at allgtag never ran. Go back to step 1.

Then click the request and read the tid parameter in the query string. That is the measurement ID receiving the data. If it is not yours, something else is tracking your site — a plugin, a theme, or an edge-injected tag such as Cloudflare Zaraz — and you are watching an empty property while data flows elsewhere.

A mistake worth avoiding Check the Network tab on your own site, not on the GA4 dashboard. Google’s interface fires its own analytics beacons with its own measurement IDs. I spent time reading five tid values that all belonged to Google, wondering why none matched mine.

Step 4: Realtime, not Reports

These are two different systems and people conflate them constantly.

RealtimeStandard reports
Delay~30 seconds24–48 hours
Use it toconfirm tracking worksanalyse behaviour
Empty meanssomething is brokenpossibly just processing

So “wait 48 hours” is bad advice as a first response. If Realtime shows you while you are on the site, everything works and the reports will populate. If Realtime is empty while you are actively browsing, waiting changes nothing.

One more thing worth knowing: GA4 cannot backfill. It only records from the moment the tag goes live. Traffic before that is gone permanently, which is why installing analytics on day one matters more than configuring it perfectly.

Step 5: Exclude yourself

Once data arrives, your own visits will distort it badly on a low-traffic site. If you get 40 visits a month and you check the site daily while working on it, most of your analytics describes you.

In GA4: Admin → Data Streams → your stream → Configure tag settings → Define internal traffic. Add a rule matching your IP address and leave traffic_type as internal, because that exact value is what the built-in filter looks for.

The step almost everyone misses Creating the rule does not activate it. Go to Admin → Data Settings → Data Filters, find “Internal Traffic”, and change its state from Testing to Active. New properties ship in Testing mode, which tags internal traffic without excluding it. Until you flip it, your own visits still count.

Note that most home connections use dynamic IP addresses that change periodically, so the rule can silently stop matching. Worth re-checking every few weeks.

Do you need Google Tag Manager?

Probably not, and the naming makes this confusing. The gtag snippet loads from googletagmanager.com, which makes people think they have installed GTM. They have not. Tag Manager has its own container ID beginning GTM-; a GA4 measurement ID begins G-.

GTM exists so non-technical marketers can add tracking without touching site code. If you control your own code and have one or two tags, it adds a layer of indirection for no benefit. It becomes genuinely worthwhile when you manage tags across client sites, run several ad platforms, or need someone non-technical to add tags safely.

The short version

Primary sources: Google’s documentation on setting up a GA4 data stream, excluding internal traffic, and installing gtag.js. For the security header side, MDN’s Content Security Policy reference is the clearest explanation of the directives above.

Related: which metrics actually matter once data arrives, what belongs in an SEO report, and check your page setup with the free analyzer.