GTM says "connected" but GA4 is empty: the usual causes
Tag Assistant says connected, Realtime shows nothing. The real reasons GA4 gets no data through GTM — in the order they actually happen — and how to check each.
TL;DR
"GTM is connected but GA4 shows nothing" is one of the most common analytics dead-ends, and it almost always comes down to a single misunderstanding: GTM loading is not the same as GA4 receiving. Tag Assistant saying connected only means the container loaded — it says nothing about whether any tag inside it fired. The usual causes, in the order they actually bite: (1) the container has changes but was never published; (2) there's no GA4 tag at all (or the wrong Measurement ID); (3) Consent Mode is holding the hit; (4) the tag's trigger is wrong; (5) you're looking at server-side events that never touch GTM; (6) an ad blocker or your own session. Below: how to tell which one you've got in about five minutes, and the fix for each.
You wired up Google Tag Manager, the GTM Preview / Tag Assistant badge says Tag Assistant Connected, you open GA4 Realtime expecting to see yourself on the page — and it's empty. Nothing. The instinct is that something is broken deep in the setup. Usually it isn't. Usually it's one of six boring things, and the reason it's confusing is that the word "connected" is doing more work than it should.
The one misunderstanding behind almost all of these
GTM is a router, not an analytics tool. Loading the container — which is all "connected" confirms — just means the dispatcher showed up to work. Whether GA4 gets a single hit depends entirely on a GA4 tag inside the container firing and sending to your property. PostHog, Plausible, or any tool you loaded directly on the page will keep working regardless, because they don't depend on GTM at all. So "my other analytics works but GA4 doesn't" is not a paradox — it's the tell that GA4's problem lives in the container, not on the page.
Hold that distinction and the six causes sort themselves out.
1. The container was never published
This is the single most common cause, and the most embarrassing once you find it. GTM has workspaces (your draft) and versions (what's live). Every change you make — adding the GA4 tag, fixing the Measurement ID, anything — sits in the workspace until you Submit → Publish. Until you do, the live container that real visitors load has none of it.
How to check: open the container; if the top bar shows unpublished changes, that's your answer. Fix: Submit → Publish. Then re-check Realtime in ~30 seconds. If you only ever tested in Preview (which uses your draft), everything looked fine while production had nothing — exactly the trap.
2. There's no GA4 tag — or the Measurement ID is wrong
If it's published and still empty, confirm a GA4 tag actually exists and points at the right property.
How to check: in GTM Preview, load the site and look at Tags Fired. You should see a Google tag / GA4 Configuration tag fire on page load. If it's not in the list, it doesn't exist or isn't triggered. If it is there, open it and confirm the Measurement ID is your real G-XXXXXXXXXX (GA4 → Admin → Data streams → Web → top right) — a blank or placeholder ID is a silent failure. Fix: add a GA4 Configuration tag on an Initialization – All Pages (or All Pages) trigger with the correct ID. A free GTM container audit will flag a container with no analytics tag in seconds.
3. Consent Mode is holding the hit
If you run Consent Mode v2 with a default-deny (most EU-facing sites should), GA4 respects it: until the visitor accepts, it sends cookieless, modeled pings rather than full hits. Depending on your property + region settings, that can look like "nothing" in a quick Realtime glance, or like oddly thin data.
How to check: accept your own cookie banner, then reload — if data appears, consent was the gate. Fix: nothing to fix if this is intended (modeled data is the point); just test with consent granted, and verify your consent-default and update commands are firing in the right order (the default must sit on the dataLayer before the container loads). If GA4 fires nothing at all even cookieless, your GA4 tag likely has an over-strict "Additional consent checks" condition — relax it to the built-in consent handling.
4. The tag fires, but on the wrong trigger
A GA4 tag on a trigger that doesn't match (a Custom Event that never fires, a path filter that excludes the page you're testing) will sit there doing nothing while the container is otherwise healthy.
How to check: GTM Preview → click your page event → is the GA4 tag under Tags Fired or Tags Not Fired? "Not fired" tells you the trigger is the problem. Fix: put the base GA4 config on Initialization – All Pages so it loads everywhere, and keep event tags on their own Custom Event triggers.
5. You're looking at events that never touch GTM
This one trips up people debugging conversions rather than page views. GTM is browser-only — it can only forward events that happen in the browser and land on the dataLayer. Anything your backend fires (a server action, a webhook, a "confirmed signup" from your auth callback) bypasses GTM entirely; it reaches GA4 only if you send it server-side via the Measurement Protocol, and it will never appear in GTM Preview no matter what.
How to check: is the event you're missing fired by a button click (browser → should be in Preview) or by your server (won't be)? Fix: for server events, verify the Measurement Protocol call + its API secret, and check GA4 DebugView, not Tag Assistant. Confusing these two delivery paths sends people hunting in the wrong tool for hours — the distinction is worth internalising once.
6. The boring one: your own browser
Before you tear the container apart: an ad/tracking blocker, a privacy browser, or a network filter on your machine will quietly drop GA4 while everything in Preview looks perfect (Preview talks to GTM's debug endpoint, not GA4's collect endpoint).
How to check: test in a clean incognito window with extensions off, or from your phone on cellular. Fix: nothing — it's local. (It's also a reminder of why client-side tracking under-counts and why high-value conversions deserve a server-side backup.)
The five-minute diagnostic, in order
- Published? Unpublished changes → Submit → Publish. (Solves it most of the time.)
- GA4 tag fires in Preview? No → add it / fix the Measurement ID.
- Accept consent + reload → appears? It was Consent Mode (working as designed).
- Tag "Not fired"? → fix the trigger (Initialization – All Pages).
- Missing a server event? → that's Measurement Protocol + DebugView, not GTM.
- Still nothing? → clean incognito / mobile to rule out your own blocker.
Why this keeps happening (and the deeper point)
None of these is hard. They're confusing because "connected" reassures you that the wrong thing is fine — the dispatcher loaded, so surely the package shipped. Tracking fails silently and specifically: a published-looking container that isn't, a tag that fires on a trigger that doesn't, a conversion that never had a browser path to begin with. That's the whole argument for treating tracking as infrastructure you verify on a schedule, not a thing you set up once and trust — the same case we make for a typed map of every property, container, and pixel with its health state, and for verifying conversions without trusting the dashboard.
Open disclosure: that verification discipline is what Phloz is built around — every client's GA4, GTM, and pixels modeled as nodes you can audit, so "is it actually firing?" has an answer that isn't a screenshot. The product case lives on CRM for SEO agencies and the pricing page; this post only needed to get your GA4 receiving data again. Start at step one — it's almost always step one.