Data Analytics2026.05.07 · 06 min read

Server-side tracking
in a cookie-less world.

GTM + Stape.io, 100% GDPR compliance, and what the infrastructure actually looks like in production at scale.

§1 The problem

When I joined Elithair, the analytics setup was a standard client-side stack: Google Tag Manager, GA4, Meta Pixel, and a handful of conversion tags firing on page events. It worked — until it didn't. Cookie consent rates dropped below 40%. Ad platforms were operating with a fraction of the signal they needed. Reported ROAS looked fine; actual performance was not.

Client-side tracking depends on user consent, which is legally required under GDPR and practically declining year-on-year. Server-side tracking solves this by moving data collection off the browser entirely — events are sent directly from your server to the platforms, without requiring third-party cookies.

Context

"Configure consent" is not a compliance strategy. Elithair operates across 40+ domains in multiple EU markets — any tracking solution had to be compliant by default, not by configuration.

§2 The architecture

The final setup has three layers: a consent management platform (CMP) at the edge, a server-side GTM container running on Stape.io, and direct API integrations to Google and Meta. No third-party cookies are set client-side after the transition.

LayerToolRole
ConsentCMP (Cookiebot)Gate signal collection at the edge
CollectionGTM server containerReceive, validate, route events
HostingStape.ioManaged sGTM infrastructure
DestinationGA4 / Meta CAPIDirect API send

§3 GTM server-side setup

The web container sends a single, unified data layer event to the server container. Routing logic lives server-side — one event can trigger GA4, Meta CAPI, and any other platform without duplicating tags in the browser.

javascript
// dataLayer push — web container
window.dataLayer.push({
  event: 'lead_submitted',
  lead_id: generateId(),
  source: document.referrer,
  consent: window.__cmp_status  // 'granted' | 'denied'
});

The server container then enriches the event with IP-derived geo data, applies consent checks, and routes to the appropriate destination. No PII leaves the server unencrypted.

§4 Stape.io hosting layer

Running a server-side GTM container yourself requires a Cloud Run or similar setup — manageable, but it adds ops overhead. Stape.io hosts the container on managed infrastructure and adds useful features: custom domains (so the tagging endpoint looks first-party), built-in GDPR tooling, and monitoring.

Watch out

Custom domain routing must be validated in Search Console if you're using the same domain as your site. Don't skip this — you'll lose organic data if GTM's gtm.js requests start returning 404s.

Matching for Meta CAPI

Meta's Conversion API requires event matching to work well. The key parameters are em (hashed email), ph (hashed phone), and fbp/fbc (cookies). Server-side, we hash PII before send:

python
import hashlib

def hash_value(val):
    # strip + lower normalisation is required by Meta CAPI spec
    return hashlib.sha256(
        val.strip().lower().encode()  
    ).hexdigest()

payload = {
    "em": hash_value(user_email),
    "ph": hash_value(user_phone),
    "event_name": "Lead",
    "event_time": int(time.time()),
}

§5 Results

After a 30-day migration period, with both stacks running in parallel to validate parity, we cut over. Key changes:

  • Reported conversions in Meta increased by ~35% — not because more happened, but because more were measured.
  • GA4 session data became consistent with server logs for the first time.
  • ROAS calculations stabilised — no more volatility from consent opt-out spikes.
  • Compliance audit passed with zero findings.

§6 What I'd do differently

The migration took longer than it needed to because we tried to maintain full parity with the legacy setup before cutting over. In retrospect, a cleaner break — accept a 2-week data gap, migrate everything at once — would have been faster and less confusing for the team reading the dashboards.

Also: document the routing logic in Stape.io before you need to debug it at 11pm. Trust me on this one.