Platform-specific guides for diagnosing and fixing analytics and tracking issues on Ghost.
Common Issues
Events Not Firing
Debug why analytics events aren't being captured on Ghost.
Overview of Ghost Tracking Issues
Ghost's modern architecture and headless CMS capabilities create unique tracking challenges. The platform's code injection system, theme structure, member authentication, and caching mechanisms can all impact analytics implementation. Understanding Ghost's routing, Handlebars templating, and content API is crucial for effective troubleshooting.
Ghost-Specific Tracking Challenges
Code Injection Limitations
Ghost provides specific code injection points with limitations:
- Site header - Runs on all pages but loads before DOM ready
- Site footer - Ideal for tracking but may fire before page fully rendered
- Post/page headers - Content-specific code injection
- Ghost Admin restrictions - No direct file system access on hosted Ghost
- Handlebars context - Template variables not accessible in injected code
- Member-only content - Tracking gated content requires special handling
Theme-Level Issues
Ghost theme architecture affects tracking:
- Handlebars partials - Tracking code in wrong partial may not execute
- Ghost helpers -
{{ghost_head}}and{{ghost_foot}}placement critical - Asset loading - Theme JavaScript may conflict with tracking scripts
- AMP pages - Accelerated Mobile Pages require different tracking approach
- Custom routes - Dynamic routes may not trigger standard page views
- Membership tiers - Paid vs free content tracking differentiation
Caching and Performance
Ghost's caching mechanisms can interfere with tracking:
- Frontend caching - Full page cache serves static HTML
- Member authentication - Signed-in state affects cache and tracking
- CDN integration - Ghost(Pro) CDN caches pages aggressively
- Service workers - PWA features may cache tracking requests
- Image optimization - Lazy-loaded images affect scroll tracking
- Accelerated Mobile Pages - AMP caching and tracking restrictions
Member and Subscription Tracking
Ghost's membership features require special consideration:
- Member authentication state - Logged in vs logged out users
- Subscription tiers - Free, paid, comp member identification
- Gated content - Tracking access to member-only posts
- Newsletter signups - Form submissions and conversions
- Portal interactions - Member portal events not automatically tracked
- Stripe integrations - Payment tracking requires additional setup
Diagnostic Checklist for Tracking Issues
Work through these steps systematically:
1. Verify Code Injection Location
// Check if tracking code is in DOM
console.log('GTM in head:', document.querySelector('head script[src*="googletagmanager.com/gtm.js"]'));
console.log('GTM in body:', document.querySelector('body script[src*="googletagmanager.com/gtm.js"]'));
// Check Ghost context
console.log('Ghost context:', typeof ghost !== 'undefined' ? ghost : 'Ghost object not available');
// Verify code injection placement
var injectedScripts = document.querySelectorAll('script:not([src])');
console.log('Inline scripts count:', injectedScripts.length);
2. Check Theme Files
Access theme files (local development or download from Admin > Design):
# Check for ghost_head and ghost_foot
grep -r "ghost_head\|ghost_foot" *.hbs
# Verify default.hbs structure
cat default.hbs | grep -A5 -B5 "ghost_head"
# Check if tracking code in theme vs code injection
grep -r "googletagmanager\|analytics" *.hbs
3. Verify Ghost Version and Features
// Check Ghost version (visible in Ghost Admin footer)
// Navigate to: /ghost/#/settings
// Check if members feature enabled
// Look for Portal button in frontend
// Test member authentication
if (typeof window.ghost !== 'undefined' && window.ghost.member) {
console.log('Member authenticated:', window.ghost.member);
} else {
console.log('No member authentication detected');
}
4. Test on Different Page Types
- Home page
- Post pages
- Page (static pages)
- Tag archives
- Author archives
- Member-only content
- AMP pages (if enabled)
5. Check for JavaScript Errors
// Monitor errors that might break tracking
window.addEventListener('error', function(e) {
console.error('JavaScript error:', e.message, e.filename, e.lineno);
});
// Check for Ghost-specific errors
if (typeof ghost !== 'undefined') {
console.log('Ghost API available');
} else {
console.warn('Ghost object not found');
}
Verifying Tracking Code Loads Correctly
Method 1: Ghost Admin Code Injection Check
- Go to Ghost Admin > Settings > Code injection
- Verify tracking code in "Site Header" or "Site Footer"
- Check that code is properly formatted (no syntax errors)
- Ensure code uses proper
<script>tags
Example proper format:
<!-- Site Header Code Injection -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
Method 2: Browser Console Verification
// Open Console (F12) and run:
// Check if dataLayer exists
if (typeof dataLayer !== 'undefined') {
console.log('✓ dataLayer exists:', dataLayer);
console.log(' Items in dataLayer:', dataLayer.length);
} else {
console.error('✗ dataLayer not found');
}
// Check Google Analytics
if (typeof gtag !== 'undefined') {
console.log('✓ gtag (GA4) found');
} else if (typeof ga !== 'undefined') {
console.log('✓ ga (Universal Analytics) found');
} else {
console.error('✗ No Google Analytics found');
}
// Check Ghost-specific context
if (typeof window.ghost !== 'undefined') {
console.log('✓ Ghost context:', window.ghost);
if (window.ghost.member) {
console.log(' Member data:', window.ghost.member);
}
}
Method 3: Network Tab Verification
- Open DevTools Network tab (F12)
- Filter by "gtm" or "analytics"
- Navigate to a page
- Look for:
gtm.js- Google Tag Manager containergtag/js- Google Analytics 4analytics.js- Universal Analyticscollect- Analytics hit requests
Verify payload includes:
- Correct page path
- Page title from Ghost post
- Custom dimensions (if configured)
Method 4: Check Theme Template
View page source and verify:
<!DOCTYPE html>
<html>
<head>
<!-- GTM should be here if in Site Header -->
<!-- Look for: -->
<script>...</script>
<!-- Or check for {{ghost_head}} output -->
{{{ghost_head}}}
</head>
<body>
<!-- GTM noscript if using GTM -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXX"></iframe></noscript>
<!-- Content -->
<!-- Tracking code here if in Site Footer -->
<!-- Look for {{ghost_foot}} output -->
{{{ghost_foot}}}
</body>
</html>
Browser Developer Tools Debugging Guide
Console Debugging
// Enable GA4 debug mode
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
// Monitor dataLayer pushes
(function() {
if (typeof dataLayer === 'undefined') {
console.error('dataLayer not initialized');
return;
}
var originalPush = dataLayer.push;
dataLayer.push = function() {
console.log('[dataLayer.push]', arguments[0]);
return originalPush.apply(dataLayer, arguments);
};
})();
// Track Ghost-specific events
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM ready, checking tracking...');
// Check if on post page
if (document.querySelector('.post-content')) {
console.log('On post page');
var postTitle = document.querySelector('.post-title');
if (postTitle) {
console.log('Post title:', postTitle.textContent);
}
}
// Check member portal
var portalLinks = document.querySelectorAll('[data-portal]');
if (portalLinks.length > 0) {
console.log('Member portal links found:', portalLinks.length);
}
});
// Monitor Ghost Portal events
if (typeof window.ghost !== 'undefined') {
window.addEventListener('message', function(e) {
if (e.data && e.data.type === 'portal') {
console.log('Ghost Portal event:', e.data);
}
});
}
Tracking Member Events
// Track member signup/signin
(function() {
var checkMember = function() {
if (typeof window.ghost !== 'undefined' && window.ghost.member) {
console.log('Member detected:', window.ghost.member);
if (typeof dataLayer !== 'undefined') {
dataLayer.push({
'event': 'member_authenticated',
'member_status': window.ghost.member.paid ? 'paid' : 'free',
'member_id': window.ghost.member.uuid
});
}
}
};
// Check immediately
checkMember();
// Check after potential portal interactions
window.addEventListener('message', function(e) {
if (e.data && e.data.type === 'portal') {
setTimeout(checkMember, 500);
}
});
})();
Network Tab Debugging
Filter for tracking requests:
gtm
analytics
collect
/g/collect
Check request headers for:
Inspect payload for:
- Page location (dl parameter)
- Document title (dt parameter)
- Custom dimensions (cd parameters)
- Events (en, ea, el parameters for UA; en parameter for GA4)
Application Tab (Cookies and Storage)
Check tracking cookies:
_ga- Google Analytics ID_gid- Session ID_gat- Throttle requests
Check Ghost cookies:
ghost-members-ssr- Member session
Verify cookie domain matches site domain.
Common Symptoms and Their Causes
| Symptom | Likely Cause | Solution |
|---|---|---|
| No tracking on any page | Code not injected or syntax error | Check code injection in Ghost Admin, verify script tags |
| Tracking only on homepage | Code injected on wrong page type | Move code to Site Header/Footer, not post/page injection |
| Delayed tracking (5-10 seconds) | Code in footer, slow theme JS | Move to header or optimize theme scripts |
| Duplicate page views | Code in both theme and code injection | Audit theme files, remove duplicate code |
| Member-only pages not tracking | Authentication state not considered | Add member context to tracking code |
| Page views work, events don't fire | Events defined in theme, not loaded | Move event tracking to code injection |
| AMP pages not tracking | Standard GA code doesn't work on AMP | Use amp-analytics component |
| Tag archives missing title | Ghost context not passed to dataLayer | Extract title from DOM or Ghost helpers |
| Portal signup not tracked | No listener for portal events | Add message event listener for portal |
| Tracking works locally, not on Ghost(Pro) | CDN caching or environment difference | Purge CDN cache, check production code injection |
| Newsletter signups not tracked | Form submission happens outside page | Use Portal message events or redirect tracking |
| Stripe payments not tracked | Payment happens in iframe | Use Stripe webhooks or Ghost webhooks |
Tag Manager Troubleshooting for Ghost
GTM Container Placement
Recommended setup in Ghost:
Site Header Code Injection:
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
<!-- End Google Tag Manager -->
Site Footer Code Injection (for noscript):
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
DataLayer Configuration for Ghost
Push Ghost-specific data to dataLayer:
<script>
// Initialize dataLayer before GTM
window.dataLayer = window.dataLayer || [];
// Push page-specific data
dataLayer.push({
'ghost_page_type': document.body.className.split(' ').find(c => c.startsWith('page-') || c.startsWith('post-') || c === 'home'),
'ghost_visibility': document.querySelector('[data-visibility]')?.getAttribute('data-visibility') || 'public',
'content_type': document.querySelector('.post-content') ? 'post' : 'page'
});
// Push member data if available
if (typeof window.ghost !== 'undefined' && window.ghost.member) {
dataLayer.push({
'member_status': window.ghost.member.paid ? 'paid' : 'free',
'member_uuid': window.ghost.member.uuid
});
}
</script>
GTM Preview Mode Issues
Problem: Preview mode doesn't connect
Solutions:
- Ensure code injection saved and published
- Clear browser cache and Ghost cache
- Check browser extensions aren't blocking GTM debugger
- Verify GTM container ID matches
Debug script:
// Check if GTM loaded correctly
console.log('GTM Container:', window.google_tag_manager);
if (window.google_tag_manager && window.google_tag_manager['GTM-XXXXXX']) {
console.log('✓ GTM container loaded');
} else {
console.error('✗ GTM container not found');
}
Variable Configuration for Ghost
Useful GTM variables for Ghost:
Post Title - Data Layer Variable or DOM scraping
- Variable Type: Data Layer Variable or DOM Element
- Name:
postTitle - Selector:
.post-titleor.article-title
Author Name - DOM scraping
- Variable Type: DOM Element
- Selector:
.author-nameor[rel="author"]
Member Status - Data Layer Variable
- Variable Type: Data Layer Variable
- Name:
member_status
Content Visibility - Data Layer Variable
- Variable Type: Data Layer Variable
- Name:
ghost_visibility
Single Page Application Behavior
While Ghost is not a true SPA, certain features behave similarly:
Infinite Scroll Tracking
Many Ghost themes use infinite scroll:
// Track infinite scroll page views
(function() {
var observedPosts = new Set();
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var post = entry.target;
var postUrl = post.querySelector('a.post-link')?.href;
if (postUrl && !observedPosts.has(postUrl)) {
observedPosts.add(postUrl);
if (typeof dataLayer !== 'undefined') {
dataLayer.push({
'event': 'virtual_pageview',
'page_path': new URL(postUrl).pathname,
'page_title': post.querySelector('.post-title')?.textContent
});
}
console.log('Infinite scroll: new post viewed', postUrl);
}
}
});
}, { threshold: 0.5 });
// Observe all post cards
document.querySelectorAll('.post-card').forEach(function(post) {
observer.observe(post);
});
})();
Portal Navigation Tracking
Track Ghost Portal interactions:
// Track portal open/close and actions
window.addEventListener('message', function(event) {
if (event.data && event.data.type && event.data.type.startsWith('portal-')) {
console.log('Portal event:', event.data);
if (typeof dataLayer !== 'undefined') {
dataLayer.push({
'event': 'portal_interaction',
'portal_action': event.data.type,
'portal_data': event.data
});
}
}
});
Cookie and Consent-Related Tracking Problems
Ghost Privacy Settings
Ghost has built-in privacy features:
- Check Ghost privacy settings: Admin > Settings > Privacy
- Verify tracking scripts not blocked by content security policy
- Check if members-only content affects tracking
Implementing Cookie Consent
Example consent banner integration:
<!-- In Site Header Code Injection -->
<script>
// Simple cookie consent check
function hasTrackingConsent() {
return localStorage.getItem('cookie_consent') === 'accepted';
}
function initializeTracking() {
if (hasTrackingConsent()) {
// Initialize GTM or GA
console.log('Tracking consent granted, initializing...');
if (typeof gtag !== 'undefined') {
gtag('consent', 'update', {
'analytics_storage': 'granted'
});
}
} else {
console.log('No tracking consent');
}
}
// Check on page load
document.addEventListener('DOMContentLoaded', initializeTracking);
// Listen for consent changes
window.addEventListener('cookie_consent_changed', function(e) {
if (e.detail === 'accepted') {
initializeTracking();
}
});
</script>
GDPR Compliance for Members
// Don't track member email or PII by default
if (window.ghost && window.ghost.member) {
// Only push anonymized member ID
dataLayer.push({
'member_id_hash': window.ghost.member.uuid, // UUID is already anonymized
'member_tier': window.ghost.member.paid ? 'paid' : 'free'
// Don't include: email, name, or other PII
});
}
Ghost-Specific Integration Conflicts
Common Theme Conflicts
jQuery version conflicts:
// Check jQuery version
console.log('jQuery version:', typeof jQuery !== 'undefined' ? jQuery.fn.jquery : 'not loaded');
// Some themes load old jQuery that conflicts with tracking
// Use noConflict if needed
var $j = jQuery.noConflict();
Theme JavaScript errors:
// Wrap tracking code in try-catch to prevent theme errors from breaking it
try {
// Your tracking code here
dataLayer.push({ /* ... */ });
} catch(e) {
console.error('Tracking error:', e);
}
Known Problematic Integrations
Ghost Search Plugins:
- Ghost Search or Sodo Search may interfere with page view tracking
- Add event listeners for search result clicks
Member Portal Customizations:
- Custom portal scripts may conflict
- Test portal interactions thoroughly
Comment Systems (Disqus, Commento):
- Comments load in iframe, won't trigger events automatically
- Use iframe communication or comment system callbacks
Event Tracking Validation Steps
1. Create Event Tracking Code
Add to Site Footer code injection:
<script>
(function() {
'use strict';
// Wait for DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
function init() {
// Track outbound links
document.addEventListener('click', function(e) {
var link = e.target.closest('a');
if (!link) return;
var href = link.href;
if (href && href.startsWith('http') && !href.includes(window.location.hostname)) {
console.log('Outbound click:', href);
if (typeof dataLayer !== 'undefined') {
dataLayer.push({
'event': 'outbound_click',
'outbound_url': href,
'outbound_text': link.textContent.trim()
});
}
}
});
// Track newsletter signup
var portalTriggers = document.querySelectorAll('[data-portal="signup"]');
portalTriggers.forEach(function(trigger) {
trigger.addEventListener('click', function() {
console.log('Portal signup clicked');
if (typeof dataLayer !== 'undefined') {
dataLayer.push({
'event': 'newsletter_signup_intent',
'signup_location': trigger.getAttribute('data-location') || 'unknown'
});
}
});
});
// Track scroll depth
var scrollDepths = [25, 50, 75, 90];
var triggered = {};
window.addEventListener('scroll', function() {
var scrollPercent = (window.scrollY + window.innerHeight) / document.body.scrollHeight * 100;
scrollDepths.forEach(function(depth) {
if (scrollPercent >= depth && !triggered[depth]) {
triggered[depth] = true;
if (typeof dataLayer !== 'undefined') {
dataLayer.push({
'event': 'scroll_depth',
'scroll_depth_threshold': depth,
'page_path': window.location.pathname
});
}
console.log('Scroll depth:', depth + '%');
}
});
});
console.log('Ghost event tracking initialized');
}
})();
</script>
2. Validate Events Fire Correctly
// Monitor all events for 30 seconds
(function() {
var events = [];
var originalPush = dataLayer.push;
dataLayer.push = function(obj) {
if (obj.event) {
events.push({
time: new Date().toISOString(),
event: obj.event,
data: obj
});
console.log('Event captured:', obj.event);
}
return originalPush.apply(dataLayer, arguments);
};
setTimeout(function() {
dataLayer.push = originalPush;
console.log('Event monitoring complete. Total events:', events.length);
console.table(events);
}, 30000);
console.log('Monitoring dataLayer events for 30 seconds...');
})();
3. Test Member-Specific Events
// Test member authentication event
if (window.ghost && window.ghost.member) {
console.log('Testing member event...');
dataLayer.push({
'event': 'test_member_event',
'member_status': window.ghost.member.paid ? 'paid' : 'free',
'test': true
});
console.log('Check GTM Preview or GA4 DebugView for event');
}
Membership and Subscription Tracking
Track Member Signups
// Monitor portal messages for successful signup
window.addEventListener('message', function(e) {
if (e.data.type === 'portal-action' && e.data.action === 'signup') {
console.log('Member signup detected:', e.data);
if (typeof dataLayer !== 'undefined') {
dataLayer.push({
'event': 'member_signup',
'signup_type': e.data.plan || 'free'
});
}
}
});
Track Content Upgrades
// Track when free members hit paywalls
document.addEventListener('DOMContentLoaded', function() {
var paywall = document.querySelector('.gh-post-upgrade-cta');
if (paywall) {
console.log('Paywall detected on page');
if (typeof dataLayer !== 'undefined') {
dataLayer.push({
'event': 'paywall_view',
'post_title': document.querySelector('.post-title')?.textContent,
'post_url': window.location.pathname
});
}
// Track upgrade button clicks
var upgradeBtn = paywall.querySelector('a[data-portal="signup"]');
if (upgradeBtn) {
upgradeBtn.addEventListener('click', function() {
dataLayer.push({
'event': 'paywall_upgrade_click',
'post_title': document.querySelector('.post-title')?.textContent
});
});
}
}
});
Track Subscription Tiers
// Differentiate tracking by member tier
function getMemberTier() {
if (!window.ghost || !window.ghost.member) {
return 'anonymous';
}
if (window.ghost.member.paid) {
return 'paid_member';
}
return 'free_member';
}
// Push to dataLayer on each page
if (typeof dataLayer !== 'undefined') {
dataLayer.push({
'user_tier': getMemberTier()
});
}
CDN and Caching Troubleshooting
Ghost(Pro) CDN Issues
Problem: Tracking code changes not appearing
Solutions:
- Purge Ghost cache: Settings > Labs > Delete all content (for testing only!)
- Wait for cache expiration: Changes may take up to 10 minutes
- Use cache-busting query parameters for testing:
- Visit:
yoursite.com/?nocache=1
- Visit:
Check if page is cached:
// Check response headers in Network tab
// Look for: X-Cache, CF-Cache-Status, Age
fetch(window.location.href)
.then(r => {
console.log('Cache headers:', {
'X-Cache': r.headers.get('X-Cache'),
'Age': r.headers.get('Age'),
'Cache-Control': r.headers.get('Cache-Control')
});
});
Custom CDN Configuration
If using Cloudflare or similar:
Bypass cache for query parameters:
- Configure cache rules to bypass for
?utm_*,?gclid=, etc.
- Configure cache rules to bypass for
Ensure HTML is not cached too aggressively:
- Set reasonable cache TTL for HTML
- Respect origin cache headers from Ghost
Purge CDN after code injection changes:
- Purge entire cache or specific URLs
- Wait 2-3 minutes for global propagation
Debugging Code Examples
Complete Ghost Diagnostic Script
(function() {
console.log('=== Ghost Analytics Diagnostic ===');
// Ghost environment
console.log('Ghost version:', document.querySelector('meta[name="generator"]')?.content || 'Unknown');
console.log('Ghost context available:', typeof window.ghost !== 'undefined');
if (window.ghost) {
console.log('Ghost config:', window.ghost);
if (window.ghost.member) {
console.log('Member authenticated:', {
uuid: window.ghost.member.uuid,
paid: window.ghost.member.paid
});
}
}
// Tracking setup checks
var checks = {
'dataLayer exists': typeof dataLayer !== 'undefined',
'dataLayer items': typeof dataLayer !== 'undefined' ? dataLayer.length : 0,
'GTM loaded': !!document.querySelector('script[src*="googletagmanager.com/gtm.js"]'),
'GA4 loaded': !!document.querySelector('script[src*="googletagmanager.com/gtag/js"]'),
'UA loaded': !!document.querySelector('script[src*="google-analytics.com/analytics.js"]'),
'jQuery available': typeof jQuery !== 'undefined',
'jQuery version': typeof jQuery !== 'undefined' ? jQuery.fn.jquery : 'N/A'
};
console.table(checks);
// Page context
var pageType = 'unknown';
if (document.body.classList.contains('home')) pageType = 'home';
else if (document.body.classList.contains('post-template')) pageType = 'post';
else if (document.body.classList.contains('page-template')) pageType = 'page';
else if (document.body.classList.contains('tag-template')) pageType = 'tag';
else if (document.body.classList.contains('author-template')) pageType = 'author';
console.log('Page type:', pageType);
console.log('Page classes:', document.body.className);
// Check for common issues
var issues = [];
if (typeof dataLayer === 'undefined') {
issues.push('dataLayer not initialized - tracking likely not working');
}
if (document.querySelectorAll('script[src*="gtm.js"]').length > 1) {
issues.push('Multiple GTM scripts detected - possible duplicate tracking');
}
var inlineScripts = Array.from(document.querySelectorAll('script:not([src])')).filter(s =>
s.textContent.includes('googletagmanager') || s.textContent.includes('analytics')
);
if (inlineScripts.length > 2) {
issues.push('Multiple inline tracking scripts - check for duplicates');
}
if (issues.length > 0) {
console.warn('Issues detected:');
issues.forEach(issue => console.warn('- ' + issue));
} else {
console.log('✓ No obvious issues detected');
}
console.log('=== Diagnostic Complete ===');
})();
Test Event Function
function testGhostEvent(eventName, eventData) {
console.log('Testing Ghost event:', eventName);
if (typeof dataLayer === 'undefined') {
console.error('dataLayer not available - tracking not initialized');
return false;
}
var eventObj = {
'event': eventName,
'timestamp': new Date().toISOString(),
'test': true,
'page_type': document.body.className.split(' ').find(c => c.includes('template')) || 'unknown'
};
// Merge custom data
Object.assign(eventObj, eventData);
dataLayer.push(eventObj);
console.log('✓ Event pushed:', eventObj);
// Verify it's in dataLayer
setTimeout(function() {
var found = dataLayer.some(item => item.event === eventName && item.test === true);
console.log(found ? '✓ Event confirmed in dataLayer' : 'Event not found in dataLayer');
}, 100);
return true;
}
// Usage examples:
testGhostEvent('test_page_view', { page_title: document.title });
testGhostEvent('test_button_click', { button_id: 'test-button' });
When to Contact Support
Contact Ghost support or your analytics vendor when:
1. Code Injection Not Working
Contact Ghost support if:
- Code saved in Admin but not appearing in page source
- Code injection randomly disappearing
- Different behavior on Ghost(Pro) vs self-hosted
Provide:
- Ghost version (from Admin footer)
- Hosting type (Ghost(Pro), Digital Ocean, self-hosted)
- Screenshot of code injection settings
- Page source showing missing code
2. Theme-Specific Issues
Contact theme developer if:
- Tracking works with default Casper theme but not custom theme
- Theme JavaScript errors breaking tracking
- Theme doesn't include
{{ghost_head}}or{{ghost_foot}}
Provide:
- Theme name and version
- Browser console errors
- Comparison with default theme
3. Member Portal Tracking Fails
Contact if:
- Portal events not accessible
- Member data not available in
window.ghost - Stripe integration tracking issues
Provide:
- Members feature configuration
- Subscription tiers setup
- Console log of
window.ghostobject
4. Performance Issues
Contact if:
- Tracking code significantly slowing page load
- Conflicts with Ghost's performance features
- Service worker caching issues
Provide:
- Lighthouse or WebPageTest results
- Network waterfall screenshots
- Tracking code used
Information to Gather Before Contacting Support
// Run this diagnostic and share output
(function() {
var diagnostics = {
ghostVersion: document.querySelector('meta[name="generator"]')?.content,
url: window.location.href,
ghostContext: typeof window.ghost !== 'undefined' ? {
apiUrl: window.ghost.apiUrl,
memberAvailable: !!window.ghost.member
} : 'not available',
trackingSetup: {
dataLayerExists: typeof dataLayer !== 'undefined',
gtmLoaded: !!document.querySelector('script[src*="gtm.js"]'),
ga4Loaded: !!document.querySelector('script[src*="gtag/js"]')
},
pageContext: {
bodyClasses: document.body.className,
title: document.title,
postContent: !!document.querySelector('.post-content')
},
errors: []
};
// Check for JS errors
var errors = [];
window.addEventListener('error', function(e) {
errors.push({
message: e.message,
source: e.filename,
line: e.lineno
});
});
setTimeout(function() {
diagnostics.errors = errors;
console.log('=== Ghost Diagnostics for Support ===');
console.log(JSON.stringify(diagnostics, null, 2));
console.log('Copy the above JSON and provide to support');
}, 3000);
})();
General Fixes
For universal tracking concepts, see the Global Tracking Issues Hub.