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]"/>
<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:
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 |