Ghost Google Analytics Integration | OpsBlu Docs

Ghost Google Analytics Integration

How to implement Google Analytics 4 on Ghost. Covers gtag.js installation, Enhanced Measurement configuration, event tracking setup, and cross-domain...

Technical reference for implementing Google Analytics 4 (GA4) on Ghost, covering code injection behavior, Handlebars helper context for post/member data, membership and subscription event tracking, and Ghost's server-rendered architecture.

How GA4 Works on Ghost

Ghost is a Node.js-based CMS that server-renders pages using the Handlebars templating engine. GA4 integration relies entirely on Ghost's code injection feature -- there is no plugin system:

  • Code injection (Settings > Code Injection): Ghost provides two injection points: Site Header (injected into <head> on every page) and Site Footer (injected before </body>). The gtag.js snippet goes in Site Header. Code injection content is stored in the Ghost database and rendered server-side into every page's HTML output.
  • Handlebars context helpers: Ghost exposes content metadata through Handlebars block helpers that can be used inline with tracking code. {{#post}}...{{/post}} provides access to {{author.name}}, {{tags}}, {{published_at}} on post pages. {{#member}}...{{/member}} exposes {{@member.status}} (free/paid/comped), {{@member.uuid}}, and {{@member.subscriptions}} for logged-in members.
  • No SPA behavior: Ghost renders each page as a full server response. Every navigation triggers a new page load, so gtag('config', ...) fires naturally on each page. No virtual pageview handling is needed.

Ghost does not provide a window.dataLayer or any built-in analytics data layer. All event parameters must be constructed using Handlebars helpers (server-side) or DOM queries (client-side). For membership/subscription tracking, the {{#member}} helper is the only way to access member state -- Ghost does not expose member data to client-side JavaScript otherwise.

Ghost themes can also include tracking code in theme template files (e.g., default.hbs), but code injection is preferred because it persists across theme changes.

Installation Steps

Step 1: Get Your GA4 Measurement ID

  1. Sign in to Google Analytics
  2. Create a new GA4 property or select an existing one
  3. Navigate to Admin > Data Streams
  4. Select your web data stream
  5. Copy your Measurement ID (format: G-XXXXXXXXXX)

Step 2: Add GA4 to Ghost

  1. Log in to your Ghost Admin panel
  2. Navigate to Settings > Code Injection
  3. In the Site Header section, paste the following code:
<!-- Google tag (gtag.js) -->
<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>
  1. Replace G-XXXXXXXXXX with your actual Measurement ID
  2. Click Save

Step 3: Verify Installation

  1. Visit your Ghost site in a new browser window
  2. Open browser DevTools (F12) and check the Console
  3. Look for successful GA4 script loading
  4. Check GA4 Real-time reports to see active visitors

Configuration Options

Enhanced Tracking for Ghost

Add enhanced tracking to capture Ghost-specific data:

<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());

  // Configure with Ghost context
  gtag('config', 'G-XXXXXXXXXX', {
    'custom_map': {
      'dimension1': 'post_author',
      'dimension2': 'post_tags',
      'dimension3': 'member_status'
    },
    'anonymize_ip': true
  });

  // Send Ghost context data
  {{#post}}
    gtag('event', 'page_view', {
      'post_author': '{{author.name}}',
      'post_tags': '{{tags}}',
      'post_published': '{{published_at}}',
      'content_type': 'post'
    });
  {{/post}}

  {{#page}}
    gtag('event', 'page_view', {
      'content_type': 'page'
    });
  {{/page}}
</script>

Implement GDPR-compliant tracking with consent mode:

<script>
  // Set default consent state
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}

  gtag('consent', 'default', {
    'analytics_storage': 'denied',
    'ad_storage': 'denied',
    'wait_for_update': 500
  });

  gtag('js', new Date());
  gtag('config', 'G-XXXXXXXXXX');

  // Update consent after user accepts
  function acceptCookies() {
    gtag('consent', 'update', {
      'analytics_storage': 'granted'
    });
  }
</script>

Member Tracking

Track Ghost member activity and subscription status:

<script>
  {{#member}}
    // Track member status
    gtag('set', 'user_properties', {
      'member_status': '{{@member.status}}',
      'subscription_tier': '{{@member.subscriptions}}',
      'member_since': '{{@member.created_at}}'
    });

    // Track member as User ID for cross-device tracking
    gtag('config', 'G-XXXXXXXXXX', {
      'user_id': '{{@member.uuid}}'
    });
  {{/member}}
</script>

Event Tracking

Automatic Content Events

Track key content interactions:

<script>
  // Track reading progress
  let scrolled25 = false, scrolled50 = false, scrolled75 = false, scrolled100 = false;

  window.addEventListener('scroll', function() {
    let scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;

    if (scrollPercent >= 25 && !scrolled25) {
      gtag('event', 'scroll_depth', {'percent': 25});
      scrolled25 = true;
    }
    if (scrollPercent >= 50 && !scrolled50) {
      gtag('event', 'scroll_depth', {'percent': 50});
      scrolled50 = true;
    }
    if (scrollPercent >= 75 && !scrolled75) {
      gtag('event', 'scroll_depth', {'percent': 75});
      scrolled75 = true;
    }
    if (scrollPercent >= 100 && !scrolled100) {
      gtag('event', 'scroll_depth', {'percent': 100});
      scrolled100 = true;
    }
  });
</script>

Newsletter Signup Tracking

Track newsletter subscription events:

<script>
  // Track newsletter signups
  document.addEventListener('DOMContentLoaded', function() {
    const subscribeForm = document.querySelector('.subscribe-form');

    if (subscribeForm) {
      subscribeForm.addEventListener('submit', function() {
        gtag('event', 'newsletter_signup', {
          'method': 'embedded_form',
          'location': window.location.pathname
        });
      });
    }
  });
</script>

Social Share Tracking

Track social media sharing:

<script>
  // Track social shares
  document.querySelectorAll('.share-link').forEach(function(link) {
    link.addEventListener('click', function() {
      const platform = this.getAttribute('data-platform');

      gtag('event', 'share', {
        'method': platform,
        'content_type': 'post',
        'item_id': window.location.pathname
      });
    });
  });
</script>

Membership & Subscription Tracking

Track Member Signups

Monitor new member registrations:

<script>
  // Add to signup success page
  {{#if success}}
    gtag('event', 'sign_up', {
      'method': 'Ghost'
    });
  {{/if}}
</script>

Track Subscription Purchases

For Ghost memberships and paid subscriptions:

<script>
  // Add to subscription confirmation
  {{#member}}
    {{#if @member.paid}}
      gtag('event', 'purchase', {
        'transaction_id': '{{@member.subscriptions.id}}',
        'value': {{@member.subscriptions.plan.amount}},
        'currency': '{{@member.subscriptions.plan.currency}}',
        'items': [{
          'item_id': '{{@member.subscriptions.plan.id}}',
          'item_name': '{{@member.subscriptions.plan.nickname}}',
          'item_category': 'Membership',
          'price': {{@member.subscriptions.plan.amount}}
        }]
      });
    {{/if}}
  {{/member}}
</script>

Troubleshooting

Tracking Not Working

Issue: No data appearing in GA4 reports

Solutions:

  1. Verify Measurement ID is correct (starts with G-)
  2. Check code injection was saved successfully
  3. Clear Ghost cache and browser cache
  4. Ensure tracking code is in Site Header, not Site Footer
  5. Test in incognito mode to rule out browser extensions
  6. Check browser console for JavaScript errors

Duplicate Page Views

Issue: Multiple page views recorded for single visit

Solutions:

  1. Check for multiple GA4 code snippets in different locations
  2. Verify theme doesn't include GA4 code separately
  3. Remove GA4 from custom theme files if using code injection
  4. Ensure GTM isn't also firing GA4 tags

Member Data Not Tracking

Issue: Member-specific events not appearing

Solutions:

  1. Verify member is logged in when testing
  2. Check member context helpers ({{#member}}) are in correct location
  3. Ensure Ghost version supports member features (3.0+)
  4. Test with GA4 DebugView for real-time validation
  5. Verify member UUID is being captured correctly

Code Injection Not Saving

Issue: Code injection changes don't persist

Solutions:

  1. Check for HTML syntax errors in injected code
  2. Verify you have admin permissions in Ghost
  3. Try injecting smaller code blocks first
  4. Clear Ghost cache: ghost restart
  5. Check Ghost logs for errors: ghost log

Performance Issues

Issue: Site loading slower after GA4 installation

Solutions:

  1. Ensure async attribute is present on script tag
  2. Consider using Google Tag Manager for better performance
  3. Load GA4 script after critical content
  4. Implement consent mode to defer tracking until accepted
  5. Use Ghost's built-in caching and CDN features

Events Not Firing on Theme

Issue: Custom events not working with specific Ghost theme

Solutions:

  1. Check theme's JavaScript for conflicts
  2. Wrap event listeners in DOMContentLoaded
  3. Use theme-specific selectors for elements
  4. Add event code to theme files instead of code injection
  5. Update Ghost and theme to latest versions

Testing and Verification

Use GA4 DebugView

Enable debug mode for detailed event tracking:

<script>
  gtag('config', 'G-XXXXXXXXXX', {
    'debug_mode': true
  });
</script>

Then check Admin > DebugView in GA4 for real-time events.

Browser Console Testing

Check console for successful tracking:

// View dataLayer contents
console.log(window.dataLayer);

// Manually fire test event
gtag('event', 'test_event', {'test_parameter': 'test_value'});

Ghost-Specific Testing

Test Ghost context data:

  1. View a post page - verify post metadata is captured
  2. Visit a page - verify page type is tracked
  3. Log in as member - verify member data is sent
  4. Subscribe to newsletter - verify signup event fires
  5. Check GA4 Real-time reports for all events