paulserban.eu

Writing Edition

Paul Serban

Writing, snippets & book notes

Cheat sheet - Post

Client-Side vs. Server-Side Tracking Architecture

← Back to Client-Side vs. Server-Side Tracking: Which Setup is Right for You?

Where your analytics tracking code executes - the user's browser or your own backend - determines your data accuracy, privacy compliance posture, page performance, and long-term engineering cost.

Overview

Key Concepts

Client-side tracking

Server-side tracking

// server enriches, validates, then forwards - client never talks to vendors directly
router.post('/api/track', async (req, res) => {
  const validation = validateEvent(req.body);
  if (!validation.valid) return res.status(400).json({ error: validation.error });
  const enriched = await enrichWithCustomerData(req.body, req.user?.id);
  analytics.track({ userId: req.user?.id, event: enriched.event, properties: enriched.properties });
  res.status(200).json({ success: true });
});

Data accuracy and reliability

Privacy and compliance architecture

Performance impact

Cost and ROI

Decision framework

Trade-offs / Caveats

Example in Practice

A purchase event illustrates the architectural split. Client-side, the button click fires gtag('event', 'purchase', {...}) directly to Google Analytics - no validation, no enrichment, no delivery guarantee. Server-side, the frontend instead posts to /api/track; the backend validates the payload (e.g. rejects purchase events missing transaction_id), enriches it with customer_lifetime_value and subscription_tier pulled from the database, applies consent-based PII scrubbing, and only then forwards it to Segment, Facebook Conversions API, and Google Ads API as appropriate. Critically, tracking is wrapped so failures never block the purchase itself:

const order = await createOrder(purchaseData);
await chargePayment(purchaseData.paymentMethod, purchaseData.total);
trackPurchaseEvent(order).catch(error => {
  console.error('Failed to track purchase event:', error); // logged, not thrown
});
return { success: true, order };

Related topics