---
title: Client-side vs. server-side tracking
description: The trade-offs between firing events from the browser and from your server, and the rule of thumb for choosing.
readingTime: 9 min
---

# Client-side vs. server-side tracking

> **Chapter 5 of the Concepts section.** Assumes you've read [Identifying users](./identifying-users.md).

Events reach your analytics tool from two places: the user's browser (client-side) or your server (server-side). Most production implementations use both, with a clear rule for which events go where.

This chapter covers the trade-offs and the rule of thumb for choosing.

## Client-side tracking

In client-side tracking, the analytics SDK runs in the user's browser. When the user does something — clicks a button, navigates a page, completes a form — the SDK packages the data and sends it directly from the browser to the analytics provider.

**What you get for free.** Client-side SDKs automatically capture rich contextual data: device type, OS, browser, screen resolution, IP-based location, page URL, page title, referrer. None of this needs custom code.

**The trade-offs.**

- **Ad blockers and privacy tools block requests.** Brave, Safari (in some modes), uBlock Origin, Ghostery, and others routinely block analytics endpoints. You typically lose 10–20% of events to this. For SaaS audiences (developers, technical users), it can be higher.
- **Events live in browser memory until they're sent.** If the user closes the tab or the page redirects before the event is dispatched, the event is lost. SDKs mitigate this with `sendBeacon` on page unload, but it's not bulletproof.
- **Events only fire while the user is in your app.** Anything that happens server-side (subscription renewals, cron-triggered actions, scheduled charges) can't be reliably reported from the client.

## Server-side tracking

In server-side tracking, events are sent from your backend directly to the analytics provider's API. The user's browser isn't involved.

**What you get.**

- **100% accuracy.** No ad blockers, no privacy tools, no dropped requests. If your server sends the event, the analytics tool gets it.
- **Works for events that happen without a user present.** Subscription renewals, scheduled jobs, webhook-driven state changes — all of these can only be reliably reported server-side.

**The trade-offs.**

- **No auto-enrichment.** Your server doesn't know the user's device, browser, IP, page URL, or session. You have to pass anything you want attached.
- **Session continuity is harder.** Server-side events don't automatically join the user's active browsing session. You have to pass the device ID and session ID explicitly (see [Identifying users](./identifying-users.md)).
- **More code.** You're maintaining tracking logic in two places — the browser and the server.

## The rule of thumb

Use **client-side** for events where:

- The user is actively in your app when the event happens
- The auto-captured context (page, device, referrer) is valuable
- A 10–20% data loss is acceptable

Use **server-side** for events where:

- The data has to be accurate (revenue, subscription state)
- The event happens asynchronously, without the user being present (webhooks, cron jobs)
- The state change is confirmed on the server (auth, database writes you want to track only on success)

For most SaaS implementations, you'll end up with:

- Page views, feature interactions, UI events → client-side
- Sign-up, sign-in, sign-out → server-side (recommended) or client-side (acceptable)
- Subscription started, trial converted, payment renewed → server-side (required)
- Feature actions that change the database → client-side, but only after server confirms the change (see [Tracking on successful states](./tracking-on-successful-states.md))

## Why "both" is the answer

A pure client-side implementation loses your most important data (revenue). A pure server-side implementation loses your richest context (page paths, devices, sessions, referrers).

The hybrid is normal. Your basic module and feature module live mostly client-side. Your revenue module lives entirely server-side. Your auth module can go either way, with server-side recommended for accuracy.

When the same event could in theory go either place, server-side wins for anything that matters (revenue, conversions, churn signals) and client-side wins for anything that's nice-to-have or context-rich (engagement, navigation, UI interactions).

## The hidden cost of asymmetry

One subtle gotcha: when you mix client and server events, you have to make sure they stitch together correctly into the same user's timeline.

If a user clicks "Start trial" in the browser (client-side event fires), then your server receives the Stripe webhook 30 seconds later (server-side event fires), Amplitude should show both events in the user's profile, in order, attributed to the same session.

That doesn't happen automatically. The server-side event needs the user's user ID, device ID, and session ID — passed through your form submission or stored in metadata along the way. The mechanics are covered in [Authentication tracking](../amplitude/authentication.md) and [Stripe revenue tracking](../amplitude/stripe-revenue.md).

If you skip this, your server-side events show up under the right user but disconnected from their browsing session. They still appear in user-based analyses, but they don't appear in session-based analyses (session duration, session funnels) and your timeline looks fragmented.
