This guide covers TYPO3-specific methods for implementing Google Analytics 4 (GA4), from beginner-friendly extensions to enterprise-grade custom implementations using TypoScript and Fluid.
Prerequisites
Before installing GA4 on TYPO3:
Create a GA4 Property in Google Analytics
- Sign in to analytics.google.com
- Create a new GA4 property
- Copy your Measurement ID (format:
G-XXXXXXXXXX)
Verify TYPO3 Version Compatibility
- TYPO3 11 LTS (PHP 7.4+)
- TYPO3 12 LTS (PHP 8.1+)
- TYPO3 13 (PHP 8.2+)
Backend Access Requirements
- Admin or appropriate permissions for:
- Extension Manager access
- TypoScript configuration
- Template editing
- Admin or appropriate permissions for:
Method 1: Extension-Based Installation
Best for: Quick setup, non-technical users, standard tracking needs
Recommended Extensions
google_analytics Extension (TER)
The most popular GA4 extension from the TYPO3 Extension Repository.
Installation via Extension Manager:
Navigate to Extension Manager
- Backend: Admin Tools → Extensions → Get Extensions
- Search for "google_analytics"
- Click Import and Install
Activate the Extension
- Admin Tools → Extensions → Installed Extensions
- Find "google_analytics"
- Click Activate (lightning icon)
Installation via Composer (Recommended for modern TYPO3):
composer require typo3-ter/google-analytics
Then activate via Extension Manager or CLI:
./vendor/bin/typo3 extension:activate google_analytics
Extension Configuration
Basic Setup
Navigate to Extension Configuration
- Admin Tools → Settings → Extension Configuration
- Select "google_analytics"
Enter Measurement ID
Measurement ID: G-XXXXXXXXXXConfigure Options
TypoScript Configuration
The extension automatically adds TypoScript, but you can customize:
# In your site's Setup field (Web → Template → Info/Modify → Setup)
plugin.tx_googleanalytics {
settings {
# Override measurement ID for specific site tree
measurementId = G-XXXXXXXXXX
# Customize tracking behavior
anonymizeIp = 1
trackOutbound = 1
trackDownloads = 1
# Exclude specific user groups
excludeUserGroups = 1,2,3
# Cookie consent integration
requireConsent = 1
consentCookieName = tracking-consent
}
}
Alternative Extensions
matomo_integration
For privacy-focused analytics:
Installation:
composer require leuchtfeuer/typo3-matomo-integration
Configuration:
plugin.tx_matomointegration {
settings {
url = https://your-matomo-instance.com/
siteId = 1
enableLinkTracking = 1
}
}
Method 2: TypoScript Implementation
Best for: Advanced users, custom implementations, multi-site setups
Basic TypoScript Setup
Add to your site template (Web → Template → Info/Modify → Setup):
# Google Analytics 4 Configuration
page.headerData.100 = TEXT
page.headerData.100.value (
<!-- Google Analytics 4 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX', {
'anonymize_ip': true,
'cookie_flags': 'SameSite=None;Secure'
});
</script>
)
Advanced TypoScript with Conditions
# Conditional GA4 loading based on site root
[siteIdentifier = "main-site"]
page.headerData.100 = TEXT
page.headerData.100.value (
<script async src="https://www.googletagmanager.com/gtag/js?id=G-MAINSITE-ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-MAINSITE-ID', {
'anonymize_ip': true
});
</script>
)
[END]
[siteIdentifier = "secondary-site"]
page.headerData.100 = TEXT
page.headerData.100.value (
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SECONDARY-ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-SECONDARY-ID');
</script>
)
[END]
Exclude Backend Users
# Only load GA4 for frontend users
[frontend.user.isLoggedIn == false]
page.headerData.100 = TEXT
page.headerData.100.value (
<!-- Google Analytics 4 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
)
[END]
TypoScript with Constants
Setup Constants (Web → Template → Info/Modify → Constants):
# Google Analytics Constants
analytics {
ga4 {
measurementId = G-XXXXXXXXXX
anonymizeIp = 1
cookieFlags = SameSite=None;Secure
}
}
Use in Setup:
page.headerData.100 = TEXT
page.headerData.100.value (
<script async src="https://www.googletagmanager.com/gtag/js?id={$analytics.ga4.measurementId}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{$analytics.ga4.measurementId}', {
'anonymize_ip': {$analytics.ga4.anonymizeIp},
'cookie_flags': '{$analytics.ga4.cookieFlags}'
});
</script>
)
Method 3: Fluid Template Integration
Best for: Full control, template-specific tracking, developers
Base Layout Template
Edit your main Fluid layout (typically: typo3conf/ext/your_sitepackage/Resources/Private/Layouts/Page.html):
<!DOCTYPE html>
<html lang="{language}">
<head>
<meta charset="utf-8">
<title>{page.title}</title>
<!-- Google Analytics 4 -->
<f:if condition="{settings.analytics.enabled}">
<script async src="https://www.googletagmanager.com/gtag/js?id={settings.analytics.measurementId}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{settings.analytics.measurementId}', {
'anonymize_ip': true,
'cookie_flags': 'SameSite=None;Secure'
});
</script>
</f:if>
<f:render section="HeaderAssets" />
</head>
<body>
<f:render section="Main" />
</body>
</html>
TypoScript Settings for Fluid
page {
10 = FLUIDTEMPLATE
10 {
templateRootPaths {
0 = EXT:your_sitepackage/Resources/Private/Templates/
}
partialRootPaths {
0 = EXT:your_sitepackage/Resources/Private/Partials/
}
layoutRootPaths {
0 = EXT:your_sitepackage/Resources/Private/Layouts/
}
settings {
analytics {
enabled = 1
measurementId = G-XXXXXXXXXX
}
}
}
}
Conditional Analytics Partial
Create a partial: Resources/Private/Partials/Analytics/GoogleAnalytics.html
<f:if condition="{settings.analytics.enabled}">
<f:if condition="{TSFE.beUserLogin} == 0">
<!-- Only load for non-backend users -->
<script async src="https://www.googletagmanager.com/gtag/js?id={settings.analytics.measurementId}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
<f:if condition="{settings.analytics.consentMode}">
// Consent Mode v2
gtag('consent', 'default', {
'analytics_storage': 'denied',
'ad_storage': 'denied',
'wait_for_update': 500
});
</f:if>
gtag('config', '{settings.analytics.measurementId}', {
'anonymize_ip': <f:if condition="{settings.analytics.anonymizeIp}" then="true" else="false" />,
'cookie_flags': '{settings.analytics.cookieFlags}',
'page_path': window.location.pathname
});
</script>
</f:if>
</f:if>
Include in your layout:
<f:render partial="Analytics/GoogleAnalytics" arguments="{_all}" />
Method 4: Site Package Integration
Best for: Enterprise setups, version control, maintainable code
Site Package Structure
your_sitepackage/
├── Configuration/
│ ├── TypoScript/
│ │ ├── constants.typoscript
│ │ └── setup.typoscript
│ └── TCA/
├── Resources/
│ ├── Private/
│ │ ├── Layouts/
│ │ ├── Partials/
│ │ │ └── Analytics/
│ │ └── Templates/
│ └── Public/
│ └── JavaScript/
│ └── ga4-events.js
└── ext_emconf.php
constants.typoscript
# cat=analytics/enable/10; type=boolean; label=Enable Google Analytics
analytics.ga4.enabled = 1
# cat=analytics/basic/20; type=string; label=GA4 Measurement ID
analytics.ga4.measurementId = G-XXXXXXXXXX
# cat=analytics/privacy/30; type=boolean; label=Anonymize IP
analytics.ga4.anonymizeIp = 1
# cat=analytics/privacy/40; type=boolean; label=Require Cookie Consent
analytics.ga4.requireConsent = 0
# cat=analytics/advanced/50; type=string; label=Cookie Flags
analytics.ga4.cookieFlags = SameSite=None;Secure
setup.typoscript
[{$analytics.ga4.enabled} == 1]
page {
includeJSFooterlibs {
ga4 = https://www.googletagmanager.com/gtag/js?id={$analytics.ga4.measurementId}
ga4.external = 1
ga4.async = 1
}
jsFooterInline {
100 = TEXT
100.value (
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{$analytics.ga4.measurementId}', {
'anonymize_ip': {$analytics.ga4.anonymizeIp},
'cookie_flags': '{$analytics.ga4.cookieFlags}'
});
)
}
# Include custom event tracking
includeJSFooter {
ga4Events = EXT:your_sitepackage/Resources/Public/JavaScript/ga4-events.js
}
}
[END]
Multi-Language and Multi-Site Configuration
Language-Specific Tracking
# Track different languages with same property
page.headerData.100 = TEXT
page.headerData.100.value (
<script>
gtag('config', 'G-XXXXXXXXXX', {
'language': '{site:language.twoLetterIsoCode}',
'content_group': '{site:language.title}'
});
</script>
)
Multi-Site Setup
Use site configurations (config/sites/*/config.yaml):
# config/sites/main/config.yaml
settings:
analytics:
measurementId: 'G-MAIN-SITE-ID'
enabled: true
# config/sites/secondary/config.yaml
settings:
analytics:
measurementId: 'G-SECONDARY-SITE-ID'
enabled: true
Access in TypoScript:
page.headerData.100 = TEXT
page.headerData.100.value (
<script async src="https://www.googletagmanager.com/gtag/js?id={site:settings.analytics.measurementId}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{site:settings.analytics.measurementId}');
</script>
)
Cookie Consent Integration
With cookieman Extension
[getTenv('HTTP_COOKIE') =~ '/cookieman=[^;]*trackingGoogleAnalytics[^;]*/' ]
# User has consented to GA4
page.headerData.100 = TEXT
page.headerData.100.value (
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
)
[END]
Consent Mode v2
page.headerData.100 = TEXT
page.headerData.100.value (
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Default consent state (denied)
gtag('consent', 'default', {
'analytics_storage': 'denied',
'ad_storage': 'denied',
'wait_for_update': 500
});
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<!-- After user consents -->
<script>
document.addEventListener('cookieConsentGranted', function() {
gtag('consent', 'update', {
'analytics_storage': 'granted'
});
});
</script>
)
Caching Considerations
TYPO3's advanced caching system requires special handling:
USER_INT for Dynamic Content
If you need uncached tracking (not recommended):
page.100 = USER_INT
page.100 {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
extensionName = YourExtension
pluginName = Analytics
}
Cache-Aware Implementation (Recommended)
# Cached GA4 implementation (works for 99% of cases)
page.headerData.100 = TEXT
page.headerData.100.value (
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
// Client-side detection for user-specific tracking
gtag('config', 'G-XXXXXXXXXX', {
'user_id': localStorage.getItem('typo3_user_id') || undefined,
'page_path': window.location.pathname
});
</script>
)
Performance Optimization
Preconnect to Google Domains
page {
headerData {
10 = TEXT
10.value (
<link rel="preconnect" href="https://www.google-analytics.com">
<link rel="preconnect" href="https://www.googletagmanager.com">
)
}
}
Delayed Loading
page.jsFooterInline.200 = TEXT
page.jsFooterInline.200.value (
// Delay GA4 loading until user interaction
let gaLoaded = false;
const loadGA = () => {
if (gaLoaded) return;
gaLoaded = true;
const script = document.createElement('script');
script.async = true;
script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX';
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
};
['mousedown', 'touchstart', 'scroll'].forEach(event => {
window.addEventListener(event, loadGA, {once: true, passive: true});
});
setTimeout(loadGA, 5000); // Fallback after 5s
)
Validation and Testing
1. Clear TYPO3 Caches
Before testing:
- Admin Tools → Maintenance → Flush Caches
- Or CLI:
./vendor/bin/typo3 cache:flush
2. Check TypoScript Template
Web → Template → Template Analyzer
- Search for "googletagmanager" or your Measurement ID
- Verify the script appears in the final output
3. Frontend Preview
Web → View (or right-click page → View)
- View page source
- Search for your Measurement ID
- Confirm gtag.js script loads
4. Real-Time Reports
Visit your website and check Google Analytics Real-Time reports
5. TYPO3 Debug Mode
In typo3conf/LocalConfiguration.php or Admin Tools → Settings:
'FE' => [
'debug' => true,
],
This shows TypoScript comments in source code for debugging.
Common TYPO3-Specific Issues
Issue: GA4 Not Loading
Causes:
- TypoScript not included in active template
- Extension not activated
- Cache not cleared
Solutions:
- Check Web → Template → Template Analyzer
- Verify extension in Admin Tools → Extensions
- Flush all caches
Issue: Tracking All Backend Users
Solution:
[backend.user.isLoggedIn == false]
# Your GA4 code here
[END]
Issue: Multi-Site Conflicts
Solution: Use site-specific settings in config/sites/*/config.yaml
Next Steps
- Configure GA4 Event Tracking for TYPO3-specific events
- Set Up E-commerce Tracking for TYPO3 shop extensions
- Debug Tracking Issues
Related Resources
- TYPO3 TypoScript Reference
- TYPO3 Fluid Documentation
- Google Tag Manager Setup - Alternative installation method