Install Google Analytics 4 on HubSpot
There are three main methods to install GA4 on your HubSpot CMS Hub site, each with different capabilities and complexity levels.
Method Comparison
| Method | Difficulty | Customization | Flexibility | Recommended For |
|---|---|---|---|---|
| Site Header HTML | Easy | Medium | Low | Quick setup, all pages |
| Google Tag Manager | Medium | High | High | Most sites (recommended) |
| Custom Module | Advanced | High | Medium | Page-specific tracking |
Method 1: Site Header HTML (Easiest)
Add GA4 globally to all HubSpot pages using the Site Header HTML setting.
Setup Steps
Get Your GA4 Measurement ID
- Open Google Analytics 4
- Go to Admin → Data Streams
- Click your web data stream
- Copy your Measurement ID (format:
G-XXXXXXXXXX)
Access Site Header Settings
- In HubSpot, go to Settings (gear icon)
- Navigate to Website → Pages
- Scroll to Site Header HTML
Add GA4 Tracking Code
Paste this code into the Site Header HTML box:
<!-- Google Analytics 4 --> <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', { 'send_page_view': true, 'cookie_flags': 'SameSite=None;Secure' }); </script>Replace
G-XXXXXXXXXXwith your Measurement ID (appears twice).Add HubSpot-Specific Parameters (Optional)
Enhance tracking with HubSpot 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()); gtag('config', 'G-XXXXXXXXXX', { 'send_page_view': true, 'cookie_flags': 'SameSite=None;Secure', {% if contact.email %} 'user_id': '{{ contact.vid }}', // HubSpot contact ID {% endif %} 'custom_map': { 'dimension1': 'lifecycle_stage', 'dimension2': 'contact_type' } }); {% if contact %} // Send contact properties as user properties gtag('set', 'user_properties', { 'lifecycle_stage': '{{ contact.lifecycle_stage }}', 'contact_type': '{% if contact.is_contact %}contact{% else %}visitor{% endif %}' }); {% endif %} </script>Save Settings
- Click Save at the bottom of the page
- Changes apply immediately to all pages
What Gets Tracked (Site Header HTML Method)
Automatically tracked:
- All page views across your entire site
- Page titles and URLs
- Referral sources
- User demographics (if enabled in GA4)
- Device and browser information
Requires additional code:
- Form submissions (see Event Tracking)
- CTA clicks
- Meeting bookings
- File downloads
- Outbound link clicks
Pros & Cons of Site Header HTML
Pros:
- Simple, one-time setup
- Works on all pages automatically
- No coding knowledge required
- Easy to maintain
Cons:
- Cannot exclude specific pages
- All customization requires updating this one code block
- HubL variables available but limited
- Harder to debug than GTM
Method 2: Google Tag Manager (Recommended)
GTM provides the most flexibility and is recommended for most HubSpot sites.
Why Use GTM?
- Easier management: Update tracking without editing HubSpot settings
- Better organization: All tags in one centralized platform
- Advanced features: Custom events, triggers, variables
- Team collaboration: Non-technical marketers can update tags
- Multiple platforms: Add GA4, Meta Pixel, and more through one container
Setup Steps
Install GTM on HubSpot
See the complete guide: Install Google Tag Manager on HubSpot
Create GA4 Configuration Tag
Once GTM is installed on your HubSpot site:
a. Create New Tag
- In GTM, go to Tags → New
- Click Tag Configuration
b. Select Tag Type
- Choose Google Analytics: GA4 Configuration
c. Configure Settings
- Measurement ID: Enter your GA4 Measurement ID (
G-XXXXXXXXXX)
d. Add HubSpot Variables (Optional)
In Configuration Settings, click Fields to Set:
Field Name Value user_id \{\{Contact ID\}\}page_type \{\{Page Type\}\}content_group \{\{Content Group\}\}You'll need to create these variables first using HubSpot's data layer (see GTM Data Layer).
e. Set Trigger
- Triggering: Select All Pages
f. Save Tag
- Name it "GA4 - Configuration"
- Click Save
Create Data Layer Variables for HubSpot Data
To pass HubSpot-specific data to GA4:
a. Create Contact ID Variable
- Go to Variables → New
- Choose Data Layer Variable
- Data Layer Variable Name:
contactId - Save as "Contact ID"
b. Create Page Type Variable
- Create new Data Layer Variable
- Data Layer Variable Name:
pageType - Save as "Page Type"
See HubSpot GTM Data Layer for implementing the data layer in HubSpot.
Test Configuration
- Click Preview in GTM
- Visit your HubSpot site
- Verify "GA4 - Configuration" tag fires on page load
- Check GA4 Realtime reports
Publish Container
- Click Submit
- Add version name: "Added GA4 Configuration"
- Click Publish
Next Steps with GTM
After GTM and GA4 are set up:
- Configure GA4 Events - Track forms, CTAs, meetings
- Set up Data Layer - Pass HubSpot data to GTM
- Configure Ecommerce - Track HubSpot Commerce (if applicable)
Method 3: Custom Module (Advanced)
Create a reusable GA4 module that can be added to specific templates or pages.
When to Use Custom Module
- Need different GA4 properties on different sections
- Want granular control over which pages have GA4
- Building a multi-site HubSpot setup
- Require template-specific configurations
Setup Steps
Access Design Tools
- Go to Marketing → Files and Templates → Design Tools
- Or use local development tools
Create New Module
- In Design Tools, right-click → New file → New module
- Name it "GA4 Tracking Module"
Add Module Code
In the module's HTML + HubL section:
{% if module.measurement_id %} <!-- Google Analytics 4 --> <script async src="https://www.googletagmanager.com/gtag/js?id={{ module.measurement_id }}"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', '{{ module.measurement_id }}', { 'send_page_view': true, {% if module.track_contact_id and contact %} 'user_id': '{{ contact.vid }}', {% endif %} {% if module.custom_dimensions %} 'custom_map': { 'dimension1': 'lifecycle_stage', 'dimension2': 'page_type' } {% endif %} }); {% if module.custom_dimensions and contact %} gtag('set', 'user_properties', { 'lifecycle_stage': '{{ contact.lifecycle_stage }}', 'page_type': '{{ content.type }}' }); {% endif %} </script> {% endif %}Add Module Fields
In the module's
fields.jsonor field editor:[ { "id": "measurement_id", "name": "measurement_id", "label": "GA4 Measurement ID", "required": true, "locked": false, "type": "text", "default": "G-XXXXXXXXXX" }, { "id": "track_contact_id", "name": "track_contact_id", "label": "Track Contact ID as User ID", "required": false, "type": "boolean", "default": true }, { "id": "custom_dimensions", "name": "custom_dimensions", "label": "Enable Custom Dimensions", "required": false, "type": "boolean", "default": false } ]Save and Publish Module
Add Module to Template
In your template (e.g.,
base.html), add before</head>:{% module "ga4_tracking" path="@hubspot/ga4_tracking_module", measurement_id="G-XXXXXXXXXX", track_contact_id=True, custom_dimensions=True %}
Pros & Cons of Custom Module
Pros:
- Reusable across multiple templates
- Configurable per-page or per-template
- Full HubL capabilities
- Can include conditional logic
Cons:
- Requires Design Tools access
- More complex to set up
- Must be added to each template manually
- Harder for non-developers to maintain
Integration with HubSpot Native Analytics
Avoiding Duplicate Tracking
HubSpot automatically tracks page views. When adding GA4, you'll have:
- HubSpot Analytics: Tracks all pages natively
- GA4: Also tracks all pages
This is expected and acceptable, as:
- HubSpot Analytics integrates with CRM and marketing tools
- GA4 provides website behavior and attribution insights
- Each serves different purposes
Data Discrepancies
Expect minor differences between HubSpot and GA4:
| Metric | HubSpot | GA4 |
|---|---|---|
| Session definition | 30 min inactivity | 30 min default (configurable) |
| User identification | Contact-based + anonymous | Client ID-based |
| Bot filtering | Automatic | Must enable |
| Referral exclusions | Pre-configured | Must configure |
Best practice: Use each platform for its strengths rather than expecting identical numbers.
Enhanced Configuration Options
IP Anonymization
Anonymize visitor IPs for privacy compliance:
gtag('config', 'G-XXXXXXXXXX', {
'anonymize_ip': true
});
Note: GA4 anonymizes IPs by default, but explicit setting ensures compliance.
Cross-Domain Tracking
If your HubSpot site spans multiple domains:
gtag('config', 'G-XXXXXXXXXX', {
'linker': {
'domains': ['example.com', 'shop.example.com']
}
});
Cookie Consent Integration
Integrate with HubSpot's cookie consent banner:
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Default consent to denied
gtag('consent', 'default', {
'analytics_storage': 'denied'
});
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
// Listen for HubSpot cookie consent
var _hsq = window._hsq = window._hsq || [];
_hsq.push(['addPrivacyConsentListener', function(consent) {
if (consent.allowed) {
// User accepted - grant consent
gtag('consent', 'update', {
'analytics_storage': 'granted'
});
}
}]);
</script>
Verification & Testing
1. Check GA4 Realtime Reports
- Open GA4 → Reports → Realtime
- Visit your HubSpot site in another browser tab
- Verify your visit appears within 30 seconds
- Navigate to different pages and verify tracking
2. Use GA4 DebugView
Enable debug mode for detailed event inspection:
Add to your GA4 configuration:
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
In GA4:
- Go to Admin → DebugView
- Visit your site
- See real-time events with full parameters
3. Verify in Browser DevTools
Check Console:
- Open browser DevTools (F12)
- Go to Console tab
- Reload page
- Look for GA4-related messages
- Verify no JavaScript errors
Check Network Tab:
- Go to Network tab
- Filter by "collect" or "google-analytics"
- Reload page
- Verify requests to
google-analytics.com/g/collect - Click request to see parameters sent
4. Test with HubSpot Preview
Before publishing changes:
- Edit any page → Click Preview
- Open DevTools and verify GA4 loads
- Check Realtime reports in GA4
Troubleshooting
GA4 Not Loading
Check:
- Measurement ID is correct (starts with
G-) - No typos in the tracking code
- Code is in Site Header HTML (not Site Footer)
- Settings have been saved
- Browser cache cleared (hard refresh: Ctrl+Shift+R)
Debug:
// Add after gtag config to verify it loaded
console.log('GA4 Loaded:', window.gtag);
console.log('Data Layer:', window.dataLayer);
Events Not Firing
See detailed troubleshooting: Events Not Firing
Quick checks:
- Verify GA4 base code is installed
- Check browser console for errors
- Disable ad blockers for testing
- Verify you're not filtering your own traffic in GA4
Duplicate Page Views
Cause: GA4 code added in multiple locations (Site Header + Template + Module)
Fix:
- Search all templates for GA4 code
- Check Site Header HTML and Site Footer HTML
- Check custom modules
- Remove all but one instance
HubL Variables Not Working
Cause: HubL syntax errors or incorrect variable names
Debug:
{# Test HubL output #}
{{ contact.email }}
{{ contact.lifecycle_stage }}
{{ content.type }}
Common issues:
- Contact variables only work for identified visitors
- Blog variables only work on blog pages
- Some variables require specific HubSpot subscriptions
Next Steps
- Configure GA4 Events - Track HubSpot forms, CTAs, and meetings
- Set up Ecommerce Tracking - Track HubSpot Commerce transactions
- Install GTM - For easier tag management
For general GA4 concepts, see Google Analytics 4 Guide.