Complete guide to diagnosing and fixing analytics event tracking issues on Episerver (Optimizely) CMS and Commerce.
Quick Diagnosis
Symptoms Checklist
Check which symptoms apply to your situation:
- No events firing at all
- Events fire in development but not production
- Events fire sometimes but not consistently
- Events fire multiple times (duplicates)
- Events fire in view mode but not after publishing
- Events fire on some browsers but not others
- Events show in browser console but not in analytics
Step-by-Step Diagnosis
Step 1: Verify Base Tracking Installation
Check GA4 Installation
// Open browser console and run:
console.log(typeof gtag);
// Should output: "function"
// Check if GA4 is loaded
console.log(window.dataLayer);
// Should show array with gtag config
If gtag is undefined:
- GA4 script not loaded
- Script blocked by ad blocker
- Script blocked by Content Security Policy
- Edit mode check preventing load
Check GTM Installation
// Open browser console and run:
console.log(typeof google_tag_manager);
// Should output: "object"
console.log(window.dataLayer);
// Should show array with GTM data
Check Meta Pixel Installation
// Open browser console and run:
console.log(typeof fbq);
// Should output: "function"
// Check pixel queue
console.log(window._fbq);
Step 2: Check Edit Mode
Most common cause: Events disabled in Episerver edit mode
Verify Edit Mode Check
@{
var isEditMode = EPiServer.Editor.PageEditing.PageIsInEditMode;
// For CMS 11: PageEditing.PageIsInEditMode(HttpContext)
}
<!-- Add temporary diagnostic -->
<div style="position: fixed; top: 0; right: 0; background: red; color: white; padding: 10px; z-index: 9999;">
Edit Mode: @isEditMode
</div>
@if (!isEditMode)
{
<!-- Tracking code here -->
}
Common Issues:
- Wrong Method (CMS 11 vs 12)
// CMS 12+ (Correct)
var isEditMode = EPiServer.Editor.PageEditing.PageIsInEditMode;
// CMS 11 (Correct)
var isEditMode = PageEditing.PageIsInEditMode(HttpContext);
// Wrong - Always false
var isEditMode = false; // Hard-coded!
- Preview Mode Confusion
var isEditMode = EPiServer.Editor.PageEditing.PageIsInEditMode;
var isPreviewMode = EPiServer.Web.ContextMode.Current == EPiServer.Web.ContextMode.Preview;
// Should tracking fire in preview?
if (!isEditMode && !isPreviewMode)
{
// Won't track in preview mode
}
Step 3: Check Browser Console for Errors
Open DevTools (F12) → Console tab
Common JavaScript Errors
Error: "gtag is not defined"
Uncaught ReferenceError: gtag is not defined
Causes:
- GA4 script not loaded
- Event fires before gtag loads
- Script blocked
Solution:
// Wrap events in check
if (typeof gtag === 'function') {
gtag('event', 'event_name', { /* params */ });
} else {
console.warn('gtag not loaded');
}
// Or wait for load
window.addEventListener('load', function() {
if (typeof gtag === 'function') {
gtag('event', 'event_name', { /* params */ });
}
});
Error: "fbq is not defined"
Solution:
// Check before calling
if (typeof fbq === 'function') {
fbq('track', 'EventName', { /* params */ });
}
Error: "Syntax error"
Uncaught SyntaxError: Unexpected token
Cause: Invalid JSON or JavaScript syntax
Check:
<!-- BAD: Missing quotes -->
<script>
gtag('event', 'click', {
value: @Model.Price // Missing quotes around string
});
</script>
<!-- GOOD: Proper syntax -->
<script>
gtag('event', 'click', {
value: @Model.Price,
item_name: '@Model.Name' // Quoted string
});
</script>
Step 4: Check Network Tab
Open DevTools (F12) → Network tab
For GA4
Filter: google-analytics.com or collect
Look for:
gtag/js?id=G-XXXXXXXXXX(GA4 script load)- Requests to
/g/collect?(event tracking)
200- Success0or(failed)- Blocked404- Incorrect URL
If no requests:
- Events not firing
- Ad blocker enabled
- CSP blocking requests
For GTM
Filter: googletagmanager.com
Look for:
gtm.js?id=GTM-XXXXXXX(GTM script)- Should see
200status
For Meta Pixel
Filter: facebook.com
Look for:
fbevents.js(Pixel script)tr?requests (event tracking)
Step 5: Check GTM Debug Mode
If using GTM:
- Click Preview in GTM container
- Enter your Episerver site URL
- GTM debugger window opens
- Perform action (e.g., click button)
- Check if event appears in debugger
Not appearing?
- Data layer push not working
- Event name mismatch
- Trigger not configured
Debug data layer:
// Console
console.log(window.dataLayer);
// Watch for pushes
var originalPush = window.dataLayer.push;
window.dataLayer.push = function() {
console.log('Data Layer Push:', arguments);
return originalPush.apply(this, arguments);
};
Step 6: Check Tag Assistants
Google Tag Assistant
- Install Google Tag Assistant
- Click "Connect" and enter URL
- Navigate and interact with site
- Review detected tags and events
Meta Pixel Helper
- Install Meta Pixel Helper
- Visit your site
- Click extension icon
- Verify pixel status and events
Common Episerver-Specific Issues
Issue 1: Events Fire in Edit Mode
Problem: Analytics tracking editors instead of visitors
Diagnosis:
<!-- Check if edit mode protection exists -->
@if (!isEditMode)
{
<script>
gtag('event', 'page_view');
</script>
}
Solution:
@{
var isEditMode = EPiServer.Editor.PageEditing.PageIsInEditMode;
}
@if (!isEditMode)
{
<!-- ALL tracking code here -->
}
Issue 2: Events Don't Fire After Publishing
Problem: Works in preview but not on live site
Causes:
- Different tracking IDs for staging/production
- Caching issues
- Preview mode check too restrictive
Solution:
Check environment-specific configuration:
// appsettings.Production.json
{
"GoogleAnalytics": {
"MeasurementId": "G-PRODUCTION"
}
}
// appsettings.Staging.json
{
"GoogleAnalytics": {
"MeasurementId": "G-STAGING"
}
}
Clear cache:
# Clear Episerver cache
# In Episerver admin: Admin → Config → Cache → Clear All
Issue 3: Commerce Events Missing Data
Problem: Ecommerce events fire but missing product data
Check data layer structure:
// Console
window.dataLayer.forEach((item, i) => {
if (item.ecommerce) {
console.log('Item ' + i + ':', item.ecommerce);
}
});
Common Issues:
// BAD: Null reference
var price = variant.GetDefaultPrice().UnitPrice.Amount;
// Throws error if no default price
// GOOD: Null check
var price = variant.GetDefaultPrice()?.UnitPrice.Amount ?? 0;
<!-- BAD: Invalid JSON -->
<script>
gtag('event', 'purchase', {
items: @Model.Items // Wrong - outputs object, not JSON
});
</script>
<!-- GOOD: Proper JSON serialization -->
<script>
gtag('event', 'purchase', {
items: @Html.Raw(JsonConvert.SerializeObject(Model.Items))
});
</script>
Issue 4: Multi-Site Wrong Tracking ID
Problem: Site A tracking to Site B's property
Diagnosis:
// Add debug output
var currentSite = SiteDefinition.Current;
var trackingId = currentSite.GetGATrackingId();
_logger.Information($"Site: {currentSite.Name}, Tracking ID: {trackingId}");
Solution:
public static string GetGATrackingId(this SiteDefinition site)
{
// Use host-based routing
return site.SiteUrl.Host switch
{
"www.site1.com" => "G-SITE1XXXX",
"www.site2.com" => "G-SITE2XXXX",
_ => null
};
}
Issue 5: Event Fires Multiple Times
Problem: Same event tracked 2-3 times
Causes:
- Multiple tracking implementations
- Event bound multiple times
- Page includes script multiple times
Diagnosis:
# Search for duplicate implementations
grep -r "gtag('event'" .
grep -r "fbq('track'" .
# Check for multiple GTM containers
grep -r "GTM-" .
Solution:
Remove duplicates and implement once:
// Prevent duplicate event binding
var eventsInitialized = false;
function initializeEvents() {
if (eventsInitialized) return;
eventsInitialized = true;
document.querySelector('#my-button').addEventListener('click', function() {
gtag('event', 'button_click');
});
}
// Call once
initializeEvents();
Content Security Policy (CSP) Issues
Diagnosis
Error in Console:
Refused to load the script 'https://www.googletagmanager.com/gtag/js'
because it violates the following Content Security Policy directive
Solution
Update CSP headers in web.config:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Content-Security-Policy"
value="
default-src 'self';
script-src 'self' 'unsafe-inline'
https://*.googletagmanager.com
https://*.google-analytics.com
https://connect.facebook.net;
img-src 'self' data:
https://*.google-analytics.com
https://*.googletagmanager.com
https://www.facebook.com;
connect-src 'self'
https://*.google-analytics.com
https://*.analytics.google.com
https://www.facebook.com;
" />
</customHeaders>
</httpProtocol>
</system.webServer>
For CMS 12+, use middleware:
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Content-Security-Policy",
"script-src 'self' 'unsafe-inline' https://*.googletagmanager.com https://*.google-analytics.com");
await next();
});
}
Ad Blocker Issues
Diagnosis
Symptoms:
- Works with ad blocker disabled
- No requests in Network tab when enabled
- Tag assistants don't detect tags
Note: This is expected behavior. Ad blockers intentionally block analytics.
Solutions
Test with ad blockers disabled
- Disable temporarily for testing
- Use incognito/private mode
- Test on different browser
Inform stakeholders
- 20-30% of users use ad blockers
- This is expected and acceptable
- Focus on users without blockers
Server-side tracking (optional)
- Implement Measurement Protocol for critical conversions
- Use server-side GTM
- Implement Facebook Conversions API
Debugging Tools and Scripts
Event Listener Logger
// Log all GTM data layer pushes
(function() {
var originalPush = Array.prototype.push;
window.dataLayer = window.dataLayer || [];
window.dataLayer.push = function() {
console.log('DataLayer Push:', arguments);
return originalPush.apply(this, arguments);
};
})();
GA4 Event Logger
// Override gtag to log all calls
(function() {
if (typeof gtag !== 'function') return;
var originalGtag = gtag;
window.gtag = function() {
console.log('gtag called:', arguments);
return originalGtag.apply(this, arguments);
};
})();
Meta Pixel Event Logger
// Log all Meta Pixel events
(function() {
if (typeof fbq !== 'function') return;
var originalFbq = fbq;
window.fbq = function() {
console.log('fbq called:', arguments);
return originalFbq.apply(this, arguments);
};
})();
Comprehensive Tracking Diagnostic
// Run this in console for complete diagnostic
(function() {
console.log('=== Tracking Diagnostic ===');
console.log('GA4 Status:', typeof gtag === 'function' ? 'Loaded' : 'NOT LOADED');
console.log('GTM Status:', typeof google_tag_manager !== 'undefined' ? 'Loaded' : 'NOT LOADED');
console.log('Meta Pixel Status:', typeof fbq === 'function' ? 'Loaded' : 'NOT LOADED');
console.log('\nData Layer:', window.dataLayer);
console.log('\nGA4 Measurement ID:');
if (window.dataLayer) {
window.dataLayer.forEach(item => {
if (item[0] === 'config') {
console.log(' ', item[1]);
}
});
}
console.log('\nCookies:');
console.log(' _ga:', document.cookie.indexOf('_ga') !== -1 ? 'Present' : 'Missing');
console.log(' _gid:', document.cookie.indexOf('_gid') !== -1 ? 'Present' : 'Missing');
console.log(' _fbp:', document.cookie.indexOf('_fbp') !== -1 ? 'Present' : 'Missing');
console.log('\n=== End Diagnostic ===');
})();
Testing Checklist
- Test in view mode (not edit mode)
- Test after clearing cache
- Test with ad blockers disabled
- Check browser console for errors
- Check Network tab for requests
- Test on multiple browsers
- Test on mobile devices
- Use tag assistant/helper extensions
- Check real-time analytics reports
- Verify in GTM debug mode (if using GTM)
When to Escalate
Contact support if:
All checks passed but events still not appearing
- Tags load correctly
- No console errors
- Network requests succeed
- Tag assistants confirm
- Still no data in analytics
Episerver-specific integration issues
- Commerce events not working
- Content Delivery API issues
- Multi-site configuration problems
Analytics platform issues
- GA4 property configuration
- GTM workspace issues
- Meta Pixel account problems