Appearance
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
Backend:
GtmDataReporter(implementingAnalyticsDataReporterinterface) pushes GA4-formatted events (purchase, add_to_cart, sign_up, etc.) into spatie'sGoogleTagManagerservice.Middleware:
GtmMiddlewareruns 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
Blade views:
tagmanager__head.blade.phpandtagmanager__body.blade.phprender the GTM container script and the dataLayer.GTM Client Container: Picks up dataLayer pushes and fires tags, which forward all GA4 events to the SGTM.
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
| Purpose | File |
|---|---|
| Interface | app/Modules/Analytics/Services/AnalyticsDataReporter.php |
| Implementation | app/Modules/Analytics/Services/GtmDataReporter.php |
| Middleware | app/Modules/Analytics/Http/Middleware/GtmMiddleware.php |
| Event enum | app/Modules/Analytics/GtmEvent.php |
| GA4 event classes | app/Modules/Analytics/Ga4/*.php (16 event classes) |
| User data | app/Modules/Analytics/Ga4/UserData.php |
| Cookie store | app/Modules/Analytics/Services/Store/CookieAnalyticsStore.php |
| Consent config | config/ixdf_analytics.php |
| GTM config | config/googletagmanager.php |
| Blade (head) | resources/views/layouts/partials/tagmanager__head.blade.php |
| Blade (body) | resources/views/layouts/partials/tagmanager__body.blade.php |
| JS dispatcher | resources/js/modules/analyticsDispatcher.js |
| JS listener | resources/js/modules/analyticsListener.js |
| Service provider | app/Modules/Analytics/Providers/MarketingServiceProvider.php |
GA4 Events Tracked
All events follow the GA4 Recommended Events spec.
| Event Name | Class | When Fired |
|---|---|---|
almost_member_info_submitted | SignupStartedEvent | AlmostMember submits info |
sign_up | SignupCompletedEvent | Member account created |
login | LoginEvent | User logs in (Email, Direct Link, SAML) |
logout | LogoutEvent | User logs out |
view_item | ViewItemEvent | Single product page viewed |
view_item_list | ViewItemListEvent | Product list impressions |
view_promotion | ViewPromotionEvent | Referral promotion viewed |
add_to_cart | AddToCartEvent | Product added to cart |
add_trial_to_cart | AddTrialToCartEvent | Trial membership added to cart |
begin_checkout | BeginCheckoutEvent | Checkout initiated |
begin_reservation_for_payment | BeginTrialAuthHoldEvent | Trial payment hold initiated |
purchase | PurchaseEvent | Purchase completed |
trial_started | TrialStartedEvent | Trial membership activated |
subscription_confirmed | SubscriptionConfirmedEvent | Newsletter signup |
member_session_identified | MemberSessionIdentifiedEvent | Authenticated session (once/day) |
Consent Management
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:
- Essential - Security, functionality, referral program, error reporting (always on)
- Analytics - GA4 analytics storage, Google bot detection
- Personalization - Ad storage, ad personalization, ad user data
- 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:
| Priority | User Types |
|---|---|
| 3 (highest) | Member, Mentor |
| 2 | AlmostMember, 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 nameph- Phone numbertype- User typecountry- 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
| Date | GTM Version | Event |
|---|---|---|
| 2016-03-09 | v1 | GTM container GTM-KJ8XGB created (a.raikovnos@gmail.com) |
| 2017-09-12 | v15 | Google Optimize tag added (A/B testing) |
| 2021-03-02 | v29 | GA4 support added |
| 2021-03-03 | v30 | Facebook Events pixel added |
| 2021-03-09 | v32 | ClickCease fraud detection setup |
| 2021-07-09 | v42 | LinkedIn and Twitter tags + Conversion Linker added |
| 2022-02-24 | v46 | Bing ads tracking codes added |
| 2022-06-22 | v47-v54 | GA4 eCommerce tracking (view_item, purchase, checkout, user ID) |
| 2022-07-01 | v55 | Bing paused |
| 2022-10-09 | v59 | Algolia Search Insights integration added |
| 2023-06-20 | v74 | Kazik added "Add to cart" tag |
| 2023-08-02 | v75 | Kazik added trial membership conversion tracking |
| 2023-11-07 | v76 | Deactivated unnecessary codes + LinkedIn change |
| 2024-01 | Mehrdad audited GTM, found 77 configs, proposed server-side migration | |
| 2024-01 | Leadership directive: move to SGTM to bypass ad blockers, 1st-party cookies | |
| 2024-01-23 | v78 | Renamed variables, tags, and triggers (cleanup begins) |
| 2024-01-25 | v79-v82 | Added customer info to Meta, FB purchase fix, login event, GAds user data |
| 2024-02-25 | v90 | Init SGTM -- server-side GTM container initialized |
| 2024-03-12 | v95 | Consent mode enabled |
| 2024-03-13 | v96 | LinkedIn CAPI enabled (server-side LinkedIn tracking) |
| 2024-03-17 | v100 | Consent enabled for Meta & LinkedIn CAPI |
| 2024-04-01 | v103 | Google Ads enhanced |
| 2024-04-26 | v108-v111 | Cleanup (parts 1-3) |
| 2024-06-10 | v115 | Added subscription_confirmed event to GA4 |
| 2024-07-15 | v116 | Disabled client-side tracking for Meta, LinkedIn, and GAds |
| 2024-07 | LinkedIn reported 0 conversions for 24h after server-side switch | |
| 2024-08 | v118-v119 | Consent mode template updated |
| 2024-09-25 | v120-v122 | Microsoft Advertising (mAds) added, including Add to Cart tracking |
| 2024-12-12 | v125 | LinkedIn client-side tracking (Web) re-enabled |
| 2025-01-02 | v126 | Meta client-side pageview re-enabled |
| 2025-01-06 | v127 | Meta events re-enabled + Algolia template updated |
| 2025-03-13 | v129 | Current 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 InsightsTotal: ~189 KB of marketing JS across 8 network requests, plus additional requests for each conversion event fired client-side.
Optimization Plan
Option A: Disable Client-Side Tags in GTM (Recommended)
Since SGTM already handles Meta CAPI, LinkedIn CAPI, and GAds server-side:
- Pause the client-side tags for Meta Pixel, LinkedIn Insight, and Bing UET in the GTM web container (
GTM-KJ8XGB) - Monitor conversion counts on each platform for 1-2 weeks
- 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:
- Debug the SGTM lookup tables and tags for Meta, LinkedIn, GAds
- Use GTM Server container preview mode to verify events are flowing
- Check each platform's event manager to confirm server-side events arrive
- 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
- Check GTM Web Container -- Log into GTM (container GTM-KJ8XGB), check which tags for Meta/LinkedIn/Bing are currently active vs paused
- Verify SGTM is running -- Confirm Meta CAPI, LinkedIn CAPI, and GAds tags are active and firing correctly in the SGTM container
- 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
- Disable & monitor -- Pause the client-side pixel tags in GTM and monitor conversion data for 1-2 weeks
Slack References
#growth-gtm-and-analyticsSlack analytics channel- Original server-side migration thread: Jan 2024 thread
Video Documentation
- Analytics Module Overview With Mehrdad and Kazik (Part 1) (July 31, 2024, 30 min)
- Analytics Module Overview With Mehrdad and Kazik (Part 2) (July 31, 2024, 38 min)