Skip to content

Analytics Architecture & Frontend Optimization

This document describes how our analytics/tracking infrastructure works end-to-end, why marketing scripts (Meta, LinkedIn, Bing) are still loaded client-side, and the plan to optimize frontend performance by removing them.

Architecture Overview

The yellow-highlighted client-side pixel scripts are candidates for removal -- their conversions should already be handled server-side by SGTM (green).

Data Flow

  1. Backend: GtmDataReporter (implementing AnalyticsDataReporter interface) pushes GA4-formatted events (purchase, add_to_cart, sign_up, etc.) into spatie's GoogleTagManager service.

  2. Middleware: GtmMiddleware runs on every web request and:

    • Sets user context (Member > AlmostMember > Lead priority)
    • Sets geolocation data
    • Restores any events from the session that weren't rendered on a previous request
    • Saves unrendered events back to session for persistence across redirects
  3. Blade views: tagmanager__head.blade.php and tagmanager__body.blade.php render the GTM container script and the dataLayer.

  4. GTM Client Container: Picks up dataLayer pushes and fires tags, which forward all GA4 events to the SGTM.

  5. SGTM (Server-Side GTM): Uses lookup tables to determine which conversion events should be forwarded to Meta, LinkedIn, and GAds via their server-side APIs (CAPI).

Key Files

PurposeFile
Interfaceapp/Modules/Analytics/Services/AnalyticsDataReporter.php
Implementationapp/Modules/Analytics/Services/GtmDataReporter.php
Middlewareapp/Modules/Analytics/Http/Middleware/GtmMiddleware.php
Event enumapp/Modules/Analytics/GtmEvent.php
GA4 event classesapp/Modules/Analytics/Ga4/*.php (16 event classes)
User dataapp/Modules/Analytics/Ga4/UserData.php
Cookie storeapp/Modules/Analytics/Services/Store/CookieAnalyticsStore.php
Consent configconfig/ixdf_analytics.php
GTM configconfig/googletagmanager.php
Blade (head)resources/views/layouts/partials/tagmanager__head.blade.php
Blade (body)resources/views/layouts/partials/tagmanager__body.blade.php
JS dispatcherresources/js/modules/analyticsDispatcher.js
JS listenerresources/js/modules/analyticsListener.js
Service providerapp/Modules/Analytics/Providers/MarketingServiceProvider.php

GA4 Events Tracked

All events follow the GA4 Recommended Events spec.

Event NameClassWhen Fired
almost_member_info_submittedSignupStartedEventAlmostMember submits info
sign_upSignupCompletedEventMember account created
loginLoginEventUser logs in (Email, Direct Link, SAML)
logoutLogoutEventUser logs out
view_itemViewItemEventSingle product page viewed
view_item_listViewItemListEventProduct list impressions
view_promotionViewPromotionEventReferral promotion viewed
add_to_cartAddToCartEventProduct added to cart
add_trial_to_cartAddTrialToCartEventTrial membership added to cart
begin_checkoutBeginCheckoutEventCheckout initiated
begin_reservation_for_paymentBeginTrialAuthHoldEventTrial payment hold initiated
purchasePurchaseEventPurchase completed
trial_startedTrialStartedEventTrial membership activated
subscription_confirmedSubscriptionConfirmedEventNewsletter signup
member_session_identifiedMemberSessionIdentifiedEventAuthenticated session (once/day)

Consent is managed via a <consent-banner> web component and stored in a client-side cookie. GTM reads this cookie through Calculated variables to gate tag firing.

Four consent categories are defined in config/ixdf_analytics.php:

  1. Essential - Security, functionality, referral program, error reporting (always on)
  2. Analytics - GA4 analytics storage, Google bot detection
  3. Personalization - Ad storage, ad personalization, ad user data
  4. Advertising - Meta Pixel, Meta CAPI, LinkedIn Insights, LinkedIn CAPI, Google Ads Tag

For server-side tracking, consent signals are forwarded from the GA4 tag to SGTM, where transformers decide whether to strip user information before forwarding to third-party APIs.

User Context & Identity

GtmMiddleware uses AnalyticsSelectiveUserRegistry to maintain a single user context with a priority system:

PriorityUser Types
3 (highest)Member, Mentor
2AlmostMember, PayingGuest
1 (lowest)Subscriber, GuestDownloader, Lead

User data is serialized via UserData class in GA4/CAPI-compatible format:

  • id - Format: {morphClass}::{userId}
  • em - Email (SHA-256 hashed for CAPI)
  • fn, ln - First/last name
  • ph - Phone number
  • type - User type
  • country - Country code

Session Persistence

The AnalyticsDataReporter interface has a design constraint: data only reaches GTM if a Blade view is rendered. The middleware mitigates this by storing unrendered events in the session and restoring them on the next request.

This ensures events fired during POST/redirect flows (e.g., purchase) are not lost.

History & Context

Timeline

DateGTM VersionEvent
2016-03-09v1GTM container GTM-KJ8XGB created (a.raikovnos@gmail.com)
2017-09-12v15Google Optimize tag added (A/B testing)
2021-03-02v29GA4 support added
2021-03-03v30Facebook Events pixel added
2021-03-09v32ClickCease fraud detection setup
2021-07-09v42LinkedIn and Twitter tags + Conversion Linker added
2022-02-24v46Bing ads tracking codes added
2022-06-22v47-v54GA4 eCommerce tracking (view_item, purchase, checkout, user ID)
2022-07-01v55Bing paused
2022-10-09v59Algolia Search Insights integration added
2023-06-20v74Kazik added "Add to cart" tag
2023-08-02v75Kazik added trial membership conversion tracking
2023-11-07v76Deactivated unnecessary codes + LinkedIn change
2024-01Mehrdad audited GTM, found 77 configs, proposed server-side migration
2024-01Leadership directive: move to SGTM to bypass ad blockers, 1st-party cookies
2024-01-23v78Renamed variables, tags, and triggers (cleanup begins)
2024-01-25v79-v82Added customer info to Meta, FB purchase fix, login event, GAds user data
2024-02-25v90Init SGTM -- server-side GTM container initialized
2024-03-12v95Consent mode enabled
2024-03-13v96LinkedIn CAPI enabled (server-side LinkedIn tracking)
2024-03-17v100Consent enabled for Meta & LinkedIn CAPI
2024-04-01v103Google Ads enhanced
2024-04-26v108-v111Cleanup (parts 1-3)
2024-06-10v115Added subscription_confirmed event to GA4
2024-07-15v116Disabled client-side tracking for Meta, LinkedIn, and GAds
2024-07LinkedIn reported 0 conversions for 24h after server-side switch
2024-08v118-v119Consent mode template updated
2024-09-25v120-v122Microsoft Advertising (mAds) added, including Add to Cart tracking
2024-12-12v125LinkedIn client-side tracking (Web) re-enabled
2025-01-02v126Meta client-side pageview re-enabled
2025-01-06v127Meta events re-enabled + Algolia template updated
2025-03-13v129Current live version (55 tags, 35 triggers, 61 variables)

Why Client-Side Pixel Scripts Still Load

In July 2024, Mehrdad disabled client-side tracking for Meta, LinkedIn, and GAds in GTM (v116) and moved conversion tracking to SGTM (server-side). However, the client-side tags were re-enabled months later:

  • Dec 12, 2024 (v125): LinkedIn Tracking (Web) re-enabled
  • Jan 2, 2025 (v126): Meta pageview re-enabled
  • Jan 6, 2025 (v127): Meta events re-enabled

The reason for re-enabling is not documented, but likely relates to conversion tracking gaps observed after the server-side transition (LinkedIn reported 0 conversions immediately after the July 2024 switch, and an expired API token was identified as a cause in Aug 2024).

Is It Duplicate Tracking?

The GTM version history confirms client-side tags are active. If the SGTM container also still has active server-side tags (Meta CAPI, LinkedIn CAPI) for the same events, this would mean duplicate tracking -- both client-side pixel scripts AND server-side CAPI firing for the same conversions.

To verify: open the SGTM container and check whether Meta CAPI, LinkedIn CAPI, and GAds tags are still active. If they are, duplicate tracking is confirmed.

Frontend Cost

Each GTM-loaded script triggers additional sub-script downloads, creating a chain:

text
GTM (gtm.js)
├── insight.min.js ·················  19.2 KB  LinkedIn Insight Tag
├── fbevents.js ····················  94.7 KB  Meta/Facebook Pixel
│   └── 44221... helper ············  37.8 KB  Meta events sub-script
├── bat.js ·························  15.5 KB  Bing UET Tag
│   └── 343154848.js ···············   1.0 KB  Bing config loader
│       └── 343154848 ··············   1.0 KB  Bing config data
│           └── 0.8.54 ·············  15.8 KB  Bing tracking runtime
└── search-insights@2.17.3 ·········   4.4 KB  Algolia Search Insights

Total: ~189 KB of marketing JS across 8 network requests, plus additional requests for each conversion event fired client-side.

Optimization Plan

Since SGTM already handles Meta CAPI, LinkedIn CAPI, and GAds server-side:

  1. Pause the client-side tags for Meta Pixel, LinkedIn Insight, and Bing UET in the GTM web container (GTM-KJ8XGB)
  2. Monitor conversion counts on each platform for 1-2 weeks
  3. If conversions are stable, delete the paused tags

This requires zero code changes -- it is a GTM configuration change only.

Savings: ~167 KB JS, 3+ network requests, eliminates the 4.23s insight.min.js load.

Option B: If SGTM Is Not Fully Working

If the client-side tags were re-enabled because SGTM wasn't reliably delivering conversions:

  1. Debug the SGTM lookup tables and tags for Meta, LinkedIn, GAds
  2. Use GTM Server container preview mode to verify events are flowing
  3. Check each platform's event manager to confirm server-side events arrive
  4. Then proceed with Option A

What Should NOT Be Removed

  • GTM Client Container itself (gtm.js) -- still needed to push dataLayer events to SGTM
  • GA4 tag -- the GA4 measurement tag fires to SGTM, the hub for all server-side tracking
  • ClickCease -- fraud detection (currently a custom HTML tag, could also move to SGTM)
  • The Laravel backend code (GtmDataReporter, middleware, GA4 events) -- source of truth for all tracking data

Action Items

  1. Check GTM Web Container -- Log into GTM (container GTM-KJ8XGB), check which tags for Meta/LinkedIn/Bing are currently active vs paused
  2. Verify SGTM is running -- Confirm Meta CAPI, LinkedIn CAPI, and GAds tags are active and firing correctly in the SGTM container
  3. Coordinate with marketing -- Before disabling client-side tags, confirm ad campaigns are seeing server-side conversions in Meta Events Manager, LinkedIn Campaign Manager, and Bing Ads
  4. Disable & monitor -- Pause the client-side pixel tags in GTM and monitor conversion data for 1-2 weeks

Slack References

Video Documentation