Overview
Segment supports two primary data collection paths: client-side (browser, mobile) and server-side (backend APIs, webhooks, databases). Each approach has distinct advantages, limitations, and use cases. Understanding when to use each - or how to coordinate both - is critical for building a robust analytics infrastructure that balances completeness, accuracy, and privacy compliance.
Client-side tracking excels at capturing user interactions, browser context, and behavioral signals. Server-side tracking ensures durability, accuracy for business-critical events, and control over what data leaves your infrastructure.
Client-Side Tracking
What Is Client-Side Tracking?
Client-side tracking instruments code that runs in the user's browser or mobile app, using Segment's Analytics.js (web) or mobile SDKs (iOS, Android, React Native).
// Client-side tracking example (Analytics.js)
analytics.track('Product Viewed', {
product_id: 'SKU-12345',
name: 'Wireless Headphones',
price: 79.99,
currency: 'USD'
});
When to Use Client-Side Tracking
| Use Case | Why Client-Side |
|---|---|
| User interactions | Button clicks, form submissions, scroll depth |
| Page views | SPA navigation, virtual page views |
| Browser context | UTM parameters, referrer, user agent, screen resolution |
| Anonymous tracking | Track users before authentication |
| Marketing attribution | Campaign parameters, landing page sources |
| Device-mode destinations | Third-party scripts (Facebook Pixel, Google Ads) that require browser execution |
| Real-time engagement | Live chat triggers, personalization, A/B testing |
Client-Side Advantages
Rich Browser Context
- Automatically captures: URL, referrer, user agent, screen size, language, timezone
- UTM parameters parsed from URL
- First-party cookies for identity persistence
Anonymous User Tracking
- Assigns
anonymousIdbefore user authenticates - Tracks pre-login behavior for attribution
- Assigns
Device-Mode Destinations
- Enables client-side integrations (Facebook Pixel, Google Ads Conversion Tracking)
- Required for retargeting pixels and conversion tracking
Real-Time Interactivity
- Powers personalization engines
- Triggers live chat based on behavior
- A/B testing frameworks
Client-Side Limitations
Ad Blockers and Privacy Extensions
Client-Side Manipulation
- Users can modify JavaScript in DevTools
- Bots and scrapers can trigger fake events
- Revenue data can be tampered with
Network Reliability
- Events lost if user closes tab before send
- Spotty mobile connections drop events
- Page unload race conditions
PII Exposure
Performance Impact
- Multiple third-party scripts slow page load
- Each destination adds HTTP requests
- Mobile data usage concerns
Client-Side Implementation
Basic Setup
<!-- Add to <head> -->
<script>
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware"];analytics.factory=function(e){return function(){var t=Array.prototype.slice.call(arguments);t.unshift(e);analytics.push(t);return analytics}};for(var e=0;e<analytics.methods.length;e++){var key=analytics.methods[e];analytics[key]=analytics.factory(key)}analytics.load=function(key,e){var t=document.createElement("script");t.type="text/javascript";t.async=!0;t.src="https://cdn.segment.com/analytics.js/v1/" + key + "/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n);analytics._loadOptions=e};analytics._writeKey="YOUR_WRITE_KEY";analytics.SNIPPET_VERSION="4.15.3";
analytics.load("YOUR_WRITE_KEY");
analytics.page();
}}();
</script>
Configuration Options
analytics.load('YOUR_WRITE_KEY', {
// Integration settings
integrations: {
'All': false, // Disable all by default
'Segment.io': true, // Enable Segment
'Google Analytics': true,
'Facebook Pixel': {
pixelId: '1234567890',
// Additional config
}
},
// Cookie configuration
cookie: {
domain: '.example.com',
maxage: 31536000, // 365 days
sameSite: 'Lax'
},
// Privacy controls
obfuscate: true, // Obfuscate URLs with sensitive data
// Middleware for event transformation
middleware: [
function(payload) {
// Remove PII before sending
if (payload.obj.properties.email) {
payload.obj.properties.email_hash = sha256(payload.obj.properties.email);
delete payload.obj.properties.email;
}
return payload;
}
]
});
Page Tracking
// Automatic page call on load
analytics.page();
// Named page call
analytics.page('Product', 'Product Detail Page', {
product_id: 'SKU-12345',
category: 'Electronics'
});
// SPA navigation tracking
window.addEventListener('popstate', function() {
analytics.page();
});
Event Tracking
// Track user actions
analytics.track('Button Clicked', {
button_id: 'cta-signup',
button_text: 'Start Free Trial',
location: 'homepage_hero'
});
// Track with callback for critical events
analytics.track('Order Completed', orderData, function() {
// Event sent, safe to redirect
window.location.href = '/confirmation';
});
Server-Side Tracking
What Is Server-Side Tracking?
Server-side tracking sends events from your backend infrastructure using Segment's HTTP API or server-side SDKs (Node.js, Python, Ruby, Go, Java, PHP, .NET).
// Server-side tracking example (Node.js)
const Analytics = require('analytics-node');
const analytics = new Analytics('YOUR_WRITE_KEY');
analytics.track({
userId: 'user_12345',
event: 'Order Completed',
properties: {
order_id: 'ORD-20240315-001',
total: 156.96,
currency: 'USD',
products: [/* ... */]
}
});
When to Use Server-Side Tracking
| Use Case | Why Server-Side |
|---|---|
| Business-critical events | Purchases, subscriptions, invoices |
| Sensitive data | Payment info, PII that shouldn't touch browser |
| Webhook-triggered events | Stripe webhooks, Salesforce updates, support ticket created |
| Batch processing | Nightly ETL jobs, data warehouse syncs |
| Server-only context | Database queries, API responses, server metrics |
| Guaranteed delivery | Events that must not be lost (revenue, compliance) |
| Attribution without client | Email clicks, SMS conversions, server-to-server attribution |
| Privacy compliance | Minimize client-side tracking, server-side controls |
Server-Side Advantages
Data Accuracy and Durability
- Events originate from authoritative source (database, payment processor)
- No client-side manipulation or fraud
- Guaranteed delivery with retries
Ad Blocker Immunity
- Bypasses browser-based blocking
- 100% event capture for critical business events
PII Control
- Sensitive data never touches browser
- Hash or redact before sending
- GDPR/CCPA compliance easier
Performance
- No impact on page load time
- No third-party scripts bloating frontend
- Better mobile experience
Flexible Context
- Include server-side context (API version, server location)
- Enrich events with database lookups
- Cross-reference with CRM or order history
Server-Side Limitations
No Browser Context
- Missing: referrer, UTM parameters, user agent, screen size
- Must manually pass context from client to server
No Anonymous Tracking
- Requires authenticated user (userId)
- Can't track pre-login behavior (unless passing anonymousId from client)
No Device-Mode Destinations
- Third-party pixels (Facebook, Google Ads) require browser execution
- Retargeting and conversion pixels won't work
- Must use cloud-mode destinations only
Implementation Complexity
- Requires backend code changes
- Must coordinate with client-side tracking
- Identity resolution across sources
Server-Side Implementation
SDK Installation
# Node.js
npm install analytics-node
# Python
pip install analytics-python
# Ruby
gem install analytics-ruby
# Go
go get github.com/segmentio/analytics-go
# Java
<!-- Maven -->
<dependency>
<groupId>com.segment.analytics.java</groupId>
<artifactId>analytics</artifactId>
<version>3.0.0</version>
</dependency>
# PHP
composer require segmentio/analytics-php
# .NET
dotnet add package Segment.Analytics
Basic Implementation (Node.js)
const Analytics = require('analytics-node');
const analytics = new Analytics('YOUR_WRITE_KEY', {
flushAt: 20, // Flush after 20 events
flushInterval: 10000 // Or every 10 seconds
});
// Track event
analytics.track({
userId: 'user_12345',
event: 'Order Completed',
properties: {
order_id: 'ORD-001',
total: 99.99,
currency: 'USD'
},
context: {
ip: req.ip,
userAgent: req.headers['user-agent']
}
});
// Identify user
analytics.identify({
userId: 'user_12345',
traits: {
email: 'user@example.com',
plan: 'premium',
created_at: '2024-01-15T10:30:00Z'
}
});
// Group for B2B
analytics.group({
userId: 'user_12345',
groupId: 'company_67890',
traits: {
name: 'Acme Corporation',
plan: 'enterprise',
employees: 500
}
});
// Flush before app shutdown
process.on('SIGTERM', () => {
analytics.flush(() => {
process.exit(0);
});
});
Webhook Integration Example
// Stripe webhook handler
app.post('/webhooks/stripe', async (req, res) => {
const event = req.body;
switch (event.type) {
case 'checkout.session.completed':
const session = event.data.object;
// Track purchase server-side
analytics.track({
userId: session.client_reference_id,
event: 'Order Completed',
properties: {
order_id: session.id,
total: session.amount_total / 100,
currency: session.currency.toUpperCase(),
payment_method: session.payment_method_types[0]
},
timestamp: new Date(event.created * 1000)
});
break;
case 'customer.subscription.created':
const subscription = event.data.object;
analytics.track({
userId: subscription.metadata.user_id,
event: 'Subscription Started',
properties: {
subscription_id: subscription.id,
plan: subscription.items.data[0].price.id,
amount: subscription.items.data[0].price.unit_amount / 100,
currency: subscription.currency.toUpperCase(),
trial_end: subscription.trial_end
}
});
break;
}
res.sendStatus(200);
});
HTTP API Direct Call
# cURL example
curl https://api.segment.io/v1/track \
-u YOUR_WRITE_KEY: \
-H 'Content-Type: application/json' \
-d '{
"userId": "user_12345",
"event": "Order Completed",
"properties": {
"order_id": "ORD-001",
"total": 99.99,
"currency": "USD"
},
"timestamp": "2024-03-15T14:30:00Z"
}'
Hybrid Approach: Best of Both Worlds
Most production implementations use both client-side and server-side tracking.
Division of Responsibilities
| Event Type | Tracked By | Reason |
|---|---|---|
| Page views | Client-side | Captures browser context, referrer, UTM |
| Button clicks | Client-side | User interaction, immediate feedback |
| Form submissions | Both | Client-side for UX, server-side for validation |
| Product views | Client-side | Behavior tracking, personalization |
| Add to cart | Both | Client-side for UX, server-side as backup |
| Checkout started | Both | Client-side for flow, server-side for accuracy |
| Order completed | Server-side | Authoritative, ad-blocker proof |
| Subscription events | Server-side | Webhook-driven, guaranteed accuracy |
| User signup | Both | Client-side captures source, server-side confirms |
| Feature usage | Client-side | Interactive, real-time |
| Error events | Both | Client-side for JS errors, server-side for API errors |
Coordinating Client and Server Identity
Passing anonymousId from Client to Server
// Client-side: Capture anonymousId before server request
const anonymousId = analytics.user().anonymousId();
// Send with form submission or AJAX
fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({
cart_id: 'cart_123',
anonymousId: anonymousId // Pass to server
})
});
// Server-side: Use client's anonymousId
app.post('/api/checkout', (req, res) => {
const { cart_id, anonymousId } = req.body;
analytics.track({
anonymousId: anonymousId, // Use client's ID
userId: req.user?.id, // Include userId if authenticated
event: 'Checkout Completed',
properties: {
cart_id: cart_id,
total: calculateTotal(cart_id)
}
});
res.json({ success: true });
});
Identity Resolution
// Client-side: User signs up
analytics.track('Signed Up', {
signup_method: 'email'
});
analytics.identify('user_12345', {
email: 'user@example.com',
name: 'Jane Doe'
});
// Server-side: Confirm signup (same userId)
analytics.identify({
userId: 'user_12345',
traits: {
email: 'user@example.com',
name: 'Jane Doe',
email_verified: true, // Server-side confirmation
created_at: new Date().toISOString()
}
});
Deduplication Strategy
When tracking the same event from both client and server, prevent duplicates:
// Client-side: Track with intent
analytics.track('Order Submitted', {
order_id: 'temp_' + Date.now(), // Temporary ID
total: 99.99,
source: 'client'
});
// Server-side: Track authoritative event
analytics.track({
userId: 'user_12345',
event: 'Order Completed', // Different event name
properties: {
order_id: 'ORD-20240315-001', // Real order ID
total: 99.99,
source: 'server'
}
});
Alternative: Use context.externalIds to link events:
// Client-side
analytics.track('Checkout Started', {
checkout_session_id: 'sess_abc123'
});
// Server-side (after payment processor confirms)
analytics.track({
userId: 'user_12345',
event: 'Order Completed',
properties: {
order_id: 'ORD-001',
total: 99.99
},
context: {
externalIds: [
{
id: 'sess_abc123',
type: 'checkout_session',
collection: 'checkout_sessions'
}
]
}
});
Decision Matrix
When to Choose Client-Side Only
- Simple content sites: Blogs, marketing sites with no transactions
- Lead generation: Form fills, downloads, newsletter signups
- Behavioral analytics: Heatmaps, session replay, user paths
- Marketing attribution: Campaign tracking, referral sources
- Budget constraints: No backend resources for server integration
When to Choose Server-Side Only
- High privacy requirements: Healthcare, finance, regulated industries
- API-first architecture: No traditional web frontend
- Data warehouse: Batch uploads from internal databases
- Ad-blocking environments: B2B audiences with high ad-blocker usage
- Revenue accuracy critical: Every transaction must be counted
When to Choose Hybrid (Recommended)
- E-commerce: Client-side for browsing, server-side for purchases
- SaaS products: Client-side for engagement, server-side for subscriptions
- Mobile apps: Client-side for in-app behavior, server-side for backend events
- Multi-touchpoint journeys: Web, mobile, email, customer support
- Compliance + personalization: Server-side for sensitive data, client-side for UX
Performance and Reliability
Client-Side Performance
Best Practices:
- Async Loading: Always load Analytics.js asynchronously
- Conditional Loading: Only load device-mode integrations when needed
- Lazy Loading: Load Analytics.js after critical page content
- Minimize Destinations: Disable unused device-mode integrations
// Lazy load Analytics.js after page interactive
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadSegment);
} else {
loadSegment();
}
function loadSegment() {
analytics.load('YOUR_WRITE_KEY');
analytics.page();
}
Server-Side Performance
Best Practices:
- Batching: Segment SDKs batch events automatically
- Async Processing: Use background jobs for non-critical events
- Retry Logic: SDKs handle retries, but monitor failed events
- Flush on Shutdown: Ensure events sent before process exits
// Background job for batch events
const queue = require('bull');
const trackingQueue = new queue('segment-tracking');
trackingQueue.process(async (job) => {
const { userId, event, properties } = job.data;
analytics.track({
userId,
event,
properties
});
});
// Enqueue event
trackingQueue.add({
userId: 'user_12345',
event: 'Feature Used',
properties: { feature: 'advanced_filters' }
});
Reliability Comparison
| Aspect | Client-Side | Server-Side |
|---|---|---|
| Ad blocker immunity | No | Yes |
| Network reliability | User's connection | Server's connection |
| Event loss risk | Moderate (tab close, errors) | Low (retries, queues) |
| Data accuracy | User can manipulate | Authoritative source |
| Compliance | PII visible in browser | Server-side controls |
Privacy and Compliance
GDPR/CCPA Considerations
Client-Side Challenges:
- Third-party cookies subject to browser restrictions
- Device-mode scripts can access full page content
- User must consent before loading marketing pixels
Server-Side Advantages:
- Full control over data collection
- Hash PII before sending
- No third-party scripts accessing user data
Consent Management
// Client-side: Conditional loading based on consent
if (userConsent.analytics) {
analytics.load('YOUR_WRITE_KEY', {
integrations: {
'Google Analytics': userConsent.marketing,
'Facebook Pixel': userConsent.marketing,
'Segment.io': true // Always send to Segment
}
});
}
// Server-side: Include consent context
analytics.track({
userId: 'user_12345',
event: 'Product Viewed',
properties: { product_id: 'SKU-123' },
context: {
consent: {
analytics: true,
marketing: false,
preferences: true
}
}
});
// Use destination filters based on consent
Troubleshooting
| Symptom | Client-Side | Server-Side |
|---|---|---|
| Events not appearing | Check Analytics.js loaded, no console errors | Verify SDK initialized, check flush() called |
| Missing browser context | N/A | Must pass from client via API request |
| Ad blocker impact | ~30% event loss | No impact |
| Duplicate events | Check for multiple page() calls | Verify deduplication logic |
| Identity fragmentation | anonymousId not persisting (cookies blocked) | anonymousId not passed from client |
| Destination not receiving | Device-mode blocked or not enabled | Cloud-mode only; device-mode won't work |
| Performance issues | Too many device-mode integrations | Batching not configured, flush interval too short |
| PII leakage | Remove PII in middleware before send | Hash or redact before track() call |
Best Practices Summary
- Use hybrid approach: Client-side for interactions, server-side for revenue
- Server-side for critical events: Purchases, subscriptions, invoices
- Client-side for attribution: UTM parameters, referrers, campaign sources
- Pass anonymousId to server: Maintain identity across sources
- Deduplicate carefully: Use different event names or externalIds
- Hash PII server-side: Don't send raw email/phone from browser
- Monitor both paths: Track client vs server event ratios
- Flush on shutdown: Ensure server-side events sent before exit
- Respect consent: Disable destinations based on user preferences
- Test both paths: Validate events appear in debugger from both sources