The GA4 tracking on-page checks open a real URL in a headless browser (Chromium via Playwright) - once before consent is accepted, once after. That gives you a view APIs alone can't provide: what actually happens on the page itself in those first few seconds?
What does the app check?
The on-page section covers seven areas:
- Server-side tagging detection - does the site respond with custom GA4 endpoints or an FPID cookie?
- GTM implementation - are containers loaded, are there duplicates?
- CMP detection - 14 consent management platforms are recognised (Cookiebot, OneTrust, Usercentrics, Borlabs, CCM19, Sourcepoint and others).
- Consent Mode v2 - the default and updated state are read along too.
- Privacy/GDPR - does tracking fire before consent is accepted?
- Third-party tools - 9 trackers are recognised (Meta, TikTok, LinkedIn, Clarity and others).
- DataLayer - are events and e-commerce data spec-compliant?
When is a check triggered?
| Check | Threshold |
|---|---|
| Measurement ID detected | Yes = green, No = red |
page_view + session_start within 2 min |
>5 = green, <2 = red |
Cross-domain cookie (_ga) |
consistent across domains = green |
Consent status analytics |
granted = green, denied = warning |
| Tracker firing before consent | every event = GDPR warning |
The logic lives in app/pages/onpage.py,
app/services/browser_check_service.py
and app/services/onpage_check_evaluator.py.
Common causes
- GTM not loaded → JavaScript error in the container, a Content Security Policy blocks the script, URL mismatch between the GA4 stream and the actual domain.
- Wrong measurement ID → copy-paste bug, an old/test ID left over in the production code.
- Tracking before consent → the GTM trigger fires on "Page Load" instead of "Consent granted", the CMP script loads after GTM or not at all.
- Cross-domain cookie missing → the
_gacookie isn't set across the sub-/main-domain setup because theautoconfiguration was overridden.
How to proceed
- In the Auditor under OnPage, enter a URL - with a Cloud Run setup the
feature may only become visible after it's activated
(
onpage.disabled.title). - Wait for the run to finish (~10-30 s per URL, since there are two phases).
- Compare the two columns "Before consent" and "After consent" - anything that fires before consent is a potential GDPR risk.
- Check CMP detection: if no CMP is found even though one is running, identify the element in the DOM and report an issue in the Auditor if needed.
- For consent mode topics, open Understanding Consent Mode v2 alongside.