Concepts
Client-side vs. server-side tracking
The trade-offs between firing events from the browser and from your server, and the rule of thumb for choosing.
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
sendBeaconon 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).
- 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)
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 and Stripe revenue tracking.
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.