How to Fix AEM Tracking Events Not Firing | OpsBlu Docs

How to Fix AEM Tracking Events Not Firing

Fix GA4, GTM, and pixel events not firing on Adobe Experience Manager — WCMMode checks, clientlib conflicts, Dispatcher cache, and AEM SPA SDK debugging

General Guide: See Global Events Not Firing Guide for universal concepts and fixes.

AEM-Specific Causes

1. WCMMode Not Checked

Tracking fires in Author mode:

<!-- Problem: No WCMMode check -->
<script>
    gtag('event', 'page_view');
</script>

2. Client Library Load Order

Scripts loaded before dependencies:

<!-- Problem: tracking before dataLayer -->
<sly data-sly-call="${clientlib.js @ categories='mysite.tracking'}"/>
<script>window.dataLayer = [];</script>

3. Dispatcher Caching Headers

Cached responses blocking cookies:

Cache-Control: public, max-age=86400
// Session cookies not set

4. CSP Blocking Analytics

Content Security Policy too restrictive:

Content-Security-Policy: script-src 'self'
// Blocks googletagmanager.com

5. HTL Context Escaping

XSS protection breaking JavaScript:

<!-- Problem: Script content escaped -->
<script>
    var data = ${dataJson}; // Escaped as text
</script>

AEM-Specific Fixes

Fix 1: Check WCMMode in HTL

Only fire tracking on Publish:

<sly data-sly-use.tracking="${'com.mysite.core.models.TrackingModel'}"
     data-sly-test="${tracking.shouldTrack}">

    <script>
        gtag('event', 'page_view', {
            'page_title': '${tracking.pageTitle}'
        });
    </script>
</sly>
@Model(adaptables = SlingHttpServletRequest.class)
public class TrackingModel {

    @Self
    private SlingHttpServletRequest request;

    public boolean shouldTrack() {
        WCMMode mode = WCMMode.fromRequest(request);
        return mode == WCMMode.DISABLED;
    }
}

Fix 2: Correct Client Library Order

Ensure proper dependency chain:

Base clientlib (loads first):

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:ClientLibraryFolder"
    categories="[mysite.datalayer]"/>

Tracking clientlib (loads after):

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:ClientLibraryFolder"
    categories="[mysite.tracking]"
    dependencies="[mysite.datalayer]"/>

In page template:

<sly data-sly-call="${clientlib.js @ categories='mysite.datalayer'}"/>
<sly data-sly-call="${clientlib.js @ categories='mysite.tracking'}"/>

Fix 3: Configure Dispatcher for Tracking

Allow cookies in cached responses:

dispatcher.any

/cache {
    /rules {
        /0001 { /type "allow" /glob "*.html" }
    }

    # Don't cache if tracking cookies present
    /invalidate {
        /0001 { /glob "*" /type "deny" }
    }
}

# Pass through tracking parameters
/filter {
    /0100 { /type "allow" /url "/content/*" /query "*_ga=*" }
}

Fix 4: Configure CSP for Analytics

Add analytics domains to CSP:

Dispatcher configuration:

/headers {
    "Content-Security-Policy" {
        "script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com;"
        "connect-src 'self' https://www.google-analytics.com https://analytics.google.com https://region1.google-analytics.com;"
        "img-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;"
    }
}

Or via Sling Rewriter:

@Component(service = TransformerFactory.class,
           property = {
               "pipeline.type=csp-transformer"
           })
public class CSPTransformerFactory implements TransformerFactory {
    // Add CSP headers
}

Fix 5: Use Proper HTL Context

Use unsafe context for JSON:

<sly data-sly-use.data="${'com.mysite.core.models.DataLayerModel'}">
    <script>
        window.dataLayer = window.dataLayer || [];
        dataLayer.push(${data.json @ context='unsafe'});
    </script>
</sly>
@Model(adaptables = SlingHttpServletRequest.class)
public class DataLayerModel {

    private String json;

    @PostConstruct
    protected void init() {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> data = new HashMap<>();
        data.put("pageType", getPageType());
        data.put("pageName", getPageName());

        try {
            this.json = mapper.writeValueAsString(data);
        } catch (JsonProcessingException e) {
            this.json = "{}";
        }
    }

    public String getJson() {
        return json;
    }
}

Fix 6: Debug with AEM Tools

Add debug logging:

@Model(adaptables = SlingHttpServletRequest.class)
public class TrackingDebugModel {

    private static final Logger LOG = LoggerFactory.getLogger(TrackingDebugModel.class);

    @Self
    private SlingHttpServletRequest request;

    @PostConstruct
    protected void init() {
        LOG.debug("WCMMode: {}", WCMMode.fromRequest(request));
        LOG.debug("Request URI: {}", request.getRequestURI());
        LOG.debug("User: {}", request.getUserPrincipal());
    }
}

Enable in OSGi Console:

com.mysite.core.models=DEBUG

Debugging Checklist

Step 1: Check WCMMode

// In browser console on Author
console.log('WCMMode from cookie:', document.cookie.includes('wcmmode'));

Step 2: Verify Client Libraries

// Check if tracking loaded
console.log('dataLayer:', window.dataLayer);
console.log('gtag:', typeof gtag);
console.log('GTM:', typeof google_tag_manager);

Step 3: Check Network Requests

In DevTools Network tab:

  • Filter by "google"
  • Look for blocked requests (red)
  • Check CSP errors in Console

Step 4: Test on Publish

Always test tracking on Publish instance:

  1. Deploy to Publish
  2. Clear Dispatcher cache
  3. Access page in incognito
  4. Check GTM Preview

Common Error Patterns

Symptom Cause Solution
Events on Author only Missing WCMMode check Add publish mode validation
No events at all Client library order Fix dependencies
Intermittent events Dispatcher caching Configure cache rules
CSP errors in console Missing domains Update CSP policy

Next Steps