Three Ways to Add GA4 to WordPress
| Method | Pros | Cons |
|---|---|---|
| Site Kit by Google | Official, easy to use | Heavy plugin |
| Manually (functions.php) | Lightweight | Requires PHP knowledge |
| Google Tag Manager | Flexible, all tags in one place | Harder to set up |
Method 2: Manually via functions.php
function add_ga4_tracking() {
$measurement_id = 'G-XXXXXXXXXX';
echo "
<script async src='https://www.googletagmanager.com/gtag/js?id={$measurement_id}'></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{$measurement_id}');
</script>";
}
add_action('wp_head', 'add_ga4_tracking');
if (!current_user_can('manage_options')) before the echo so your own visits are not counted in analytics.Setting Up GA4 Goals
In GA4, goals are called Events. The key events for a hosting site are:
- purchase — order placed
- sign_up — registration
- generate_lead — form submission
- page_view — page view
Diagnosing GA4 tracking errors
The tracking code is in place, but no data shows up in the reports. The difference between GA4 and the old Universal Analytics is that GA4 groups everything into events, so many "errors" are not really bugs but just a different data model. Here is what the typical symptoms mean.
| Symptom | Cause | What to do |
|---|---|---|
| The Real-Time report is empty even though the code is present | The Measurement ID has a typo, or the script is blocked by a cache plugin as "unused JS" | Check the ID in GA4 → Admin → Data Streams, exclude the file from minification |
| Console error: gtag is not defined | gtag.js did not finish loading before gtag() was called, usually because the include order was changed | Do not change the order from the example: dataLayer first, then the gtag.js script itself |
| Session count is twice what is expected | GA4 is wired up both through Site Kit and manually via functions.php | Keep only one way of adding the tracking code |
| A request to google-analytics.com is marked Blocked by client | An ad blocker is stopping the request for part of the visitors | This is expected for a share of traffic; for reliability use server-side tagging with a GTM Server Container |
When a manual GA4 setup is not enough
- Sites with strict GDPR requirements — you need Consent Mode v2 and a consent banner before any data is sent to GA4
- Online stores with sales reporting — passing purchase data manually via functions.php is hard; the built-in WooCommerce integration or GTM is easier
- Companies with a legal requirement to keep data inside the EU — this needs a separate data processing region setting in GA4 Admin, not just inserting the code
- One-page sites with dozens of micro-conversions — manually tagging every event in code does not scale; you need Google Tag Manager
- Sites with several domains under one brand — cross-domain reporting needs cross-domain measurement configured, not just the same code pasted on every domain
There is a separate problem of trusting the numbers. Even when the tracking code works technically, comparing conversions before and after a site redesign is only valid with the same traffic source: paid traffic and organic traffic convert differently, and mixing them in one report distorts the conclusions.
Setting it up with Google Tag Manager
GTM is convenient because every tag is managed from one interface without touching code for every change.
<!-- In head, right after the opening body tag -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- The main container is inserted into head via functions.php,
the same way as in the gtag example above -->
If the GTM container fails to load because of a content security policy, check the CSP setup on the site.
How to check that data reaches GA4 correctly
- In GA4 → Admin → DebugView, events appear in real time while debugging is active
- In DevTools → Network, the request to google-analytics.com/g/collect returns status 204
- The Tag Assistant browser extension shows the tags firing with no configuration errors
- The number of page_view events roughly matches the number of unique page loads for the same period
// Quick check in the browser console
console.log(window.dataLayer);
// The array should contain gtag('config', ...) and gtag('event', ...) entries
After checking the tracking, it is also worth reviewing the basic SEO setup — see the article on Yoast SEO.