Overview
Simple Analytics offers flexible tracking approaches tailored to different use cases and technical requirements. Understanding the differences between client-side JavaScript tracking and server-side API tracking helps you choose the best implementation for your specific needs while maintaining Simple Analytics' privacy-first philosophy.
Both tracking methods provide the same privacy guarantees - no cookies, no user tracking, full GDPR compliance - but offer different trade-offs in terms of implementation complexity, ad-blocker resistance, and data accuracy.
Client-Side Tracking
Client-side tracking uses JavaScript that runs in visitors' browsers to automatically collect and send pageview data to Simple Analytics.
Basic Implementation
The standard Simple Analytics script:
<script async defer src="https://scripts.simpleanalyticscdn.com/latest.js"></script>
<noscript><img src="https://queue.simpleanalyticscdn.com/noscript.gif" alt="" referrerpolicy="no-referrer-when-downgrade" /></noscript>
Custom Hostname
For proxy implementations or custom domains:
<script async defer src="https://scripts.simpleanalyticscdn.com/latest.js" data-hostname="example.com"></script>
Automatic Features
Client-side tracking automatically provides:
- Pageview Tracking: Every page load is recorded
- Referrer Detection: Where visitors came from
- Screen Resolution: Device viewport data
- Language Detection: Browser language preferences
- Path Tracking: Which pages are visited
Client-Side Advantages
- Simple Setup: Just add one script tag
- Automatic Tracking: No manual pageview calls needed
- Rich Context: Access to full browser environment
- SPA Support: Built-in support for hash-based routing
- No Server Load: Processing happens in the browser
- Noscript Fallback: Image pixel for non-JavaScript users
Client-Side Limitations
- Ad Blocker Susceptibility: Can be blocked by privacy extensions
- JavaScript Dependency: Requires JavaScript enabled
- Client Performance: Minor page load overhead
- Browser Restrictions: Subject to browser security policies
Server-Side Tracking
Server-side tracking sends analytics data directly from your application server to Simple Analytics API, bypassing the browser entirely.
API Endpoint
POST https://queue.simpleanalyticscdn.com/post
Basic Implementation
Send pageview from backend:
curl -X POST https://queue.simpleanalyticscdn.com/post \
-H "Content-Type: application/json" \
-H "User-Agent: Mozilla/5.0..." \
-d '{
"type": "pageview",
"hostname": "example.com",
"path": "/page",
"referrer": "https://google.com"
}'
Node.js Implementation
const fetch = require('node-fetch');
async function trackPageview(req) {
try {
await fetch('https://queue.simpleanalyticscdn.com/post', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': req.headers['user-agent'] || 'Unknown'
},
body: JSON.stringify({
type: 'pageview',
hostname: 'example.com',
path: req.path,
referrer: req.headers.referer || '',
ua: req.headers['user-agent']
})
});
} catch (error) {
console.error('Simple Analytics error:', error);
}
}
// Express middleware
app.use((req, res, next) => {
trackPageview(req);
next();
});
Python/Flask Implementation
import requests
def track_pageview(request):
try:
requests.post(
'https://queue.simpleanalyticscdn.com/post',
json={
'type': 'pageview',
'hostname': 'example.com',
'path': request.path,
'referrer': request.headers.get('Referer', ''),
'ua': request.headers.get('User-Agent', '')
},
headers={
'Content-Type': 'application/json',
'User-Agent': request.headers.get('User-Agent', 'Unknown')
}
)
except Exception as e:
print(f"Simple Analytics error: {e}")
# Flask before_request
@app.before_request
def before_request():
track_pageview(request)
PHP Implementation
<?php
function trackPageview() {
$data = [
'type' => 'pageview',
'hostname' => 'example.com',
'path' => $_SERVER['REQUEST_URI'],
'referrer' => $_SERVER['HTTP_REFERER'] ?? '',
'ua' => $_SERVER['HTTP_USER_AGENT'] ?? ''
];
$ch = curl_init('https://queue.simpleanalyticscdn.com/post');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'User-Agent: ' . ($_SERVER['HTTP_USER_AGENT'] ?? 'Unknown')
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
trackPageview();
?>
Ruby/Rails Implementation
require 'net/http'
require 'json'
def track_pageview(request)
uri = URI('https://queue.simpleanalyticscdn.com/post')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Post.new(uri.path, {
'Content-Type' => 'application/json',
'User-Agent' => request.user_agent || 'Unknown'
})
req.body = {
type: 'pageview',
hostname: 'example.com',
path: request.path,
referrer: request.referer || '',
ua: request.user_agent || ''
}.to_json
http.request(req)
rescue => e
Rails.logger.error "Simple Analytics error: #{e}"
end
# Rails before_action
before_action :track_analytics
def track_analytics
track_pageview(request)
end
Server-Side Advantages
- Ad Blocker Immunity: Cannot be blocked by browser extensions
- Guaranteed Tracking: Works regardless of client settings
- API Endpoints: Track server-only actions (downloads, API calls)
- Full Control: Complete control over what data is sent
- Privacy Enhanced: No client-side scripts or fingerprinting
- Backend Context: Access to server-side session data
Server-Side Limitations
- Manual Implementation: Requires backend code changes
- Server Resources: Adds load to application server
- No Automatic Features: Must manually track each pageview
- Limited Context: No access to browser-specific data
- Complexity: More complex than script tag
Hybrid Approach
Combine both methods for comprehensive coverage:
<!-- Client-side for automatic pageview tracking -->
<script async defer src="https://scripts.simpleanalyticscdn.com/latest.js"></script>
// Server-side for critical actions
app.post('/api/subscribe', async (req, res) => {
// Track server-side for reliability
await trackEvent('subscription', {
plan: req.body.plan
});
res.json({ success: true });
});
Choosing the Right Approach
Use Client-Side When:
- Static Websites: Content sites, blogs, portfolios
- Quick Setup: You need analytics running immediately
- Standard Tracking: Pageviews and basic referrer data suffice
- Limited Backend: No server-side code or static hosting
- SPA Applications: Single-page apps with client-side routing
Example Use Cases:
- Marketing websites
- Documentation sites
- Blogs and content platforms
- Static site generators (Gatsby, Hugo, Jekyll)
Use Server-Side When:
- Ad Blocker Concerns: Significant portion of users use ad blockers
- API Services: Tracking server-only endpoints
- Critical Conversions: Must track important events reliably
- Downloads: Tracking file downloads or PDF views
- Privacy Maximum: Want zero client-side tracking
- Server-Rendered: Primarily server-side rendered application
Example Use Cases:
- SaaS applications
- API-first services
- Download portals
- Privacy-critical applications
- B2B tools with tech-savvy users
Use Hybrid Approach When:
- Maximum Coverage: Want comprehensive data collection
- Both Contexts: Need both client and server data
- Fallback Required: Critical tracking needs redundancy
- Mixed Architecture: Combination of static and dynamic pages
Example Use Cases:
- E-commerce platforms
- Enterprise applications
- Complex web applications
- Sites with both public and authenticated sections
Implementation Examples
SPA Route Tracking (Client-Side)
For React Router:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function usePageViews() {
let location = useLocation();
useEffect(() => {
if (window.sa_pageview) {
window.sa_pageview();
}
}, [location]);
}
For Vue Router:
router.afterEach((to, from) => {
if (window.sa_pageview) {
window.sa_pageview();
}
});
Download Tracking (Server-Side)
app.get('/download/:file', async (req, res) => {
// Track download server-side
await trackEvent('download', {
file: req.params.file
});
// Serve file
res.download(`./files/${req.params.file}`);
});
Form Submission (Hybrid)
// Client-side event
document.getElementById('contact-form').addEventListener('submit', function(e) {
sa_event('form_submit_attempt');
});
// Server-side confirmation
app.post('/api/contact', async (req, res) => {
// Track successful submission server-side
await trackEvent('form_submit_success', {
form_type: 'contact'
});
res.json({ success: true });
});
Validation and Testing
Test Client-Side Tracking
// 1. Check script loaded
console.log(typeof sa_pageview !== 'undefined' ? 'Loaded' : 'Not loaded');
// 2. Manually trigger pageview
if (window.sa_pageview) window.sa_pageview();
// 3. Check Network tab
// Look for requests to queue.simpleanalyticscdn.com
Test Server-Side Tracking
# Test with curl
curl -X POST https://queue.simpleanalyticscdn.com/post \
-H "Content-Type: application/json" \
-H "User-Agent: TestAgent/1.0" \
-d '{
"type": "pageview",
"hostname": "example.com",
"path": "/test"
}' \
-v
Dashboard Verification
- Wait 2-5 minutes for data processing
- Check Simple Analytics dashboard
- Verify pageviews appear
- Confirm correct paths and referrers
Troubleshooting
| Issue | Method | Cause | Solution |
|---|---|---|---|
| No pageviews appearing | Client-side | Script blocked or not loading | Check browser console; verify script URL |
| Ad blocker blocking | Client-side | Browser extension active | Switch to server-side or use proxy |
| 400 Bad Request | Server-side | Invalid payload format | Verify JSON structure matches API spec |
| Duplicate pageviews | Hybrid | Both methods tracking same action | Implement deduplication logic |
| Missing user agent | Server-side | Header not passed | Ensure UA header is included |
| CORS errors | Client-side | Security policy issue | Simple Analytics handles CORS; check network |
| Server load high | Server-side | Too many sync requests | Use async tracking or batching |
| Path not showing | Both | Path not sent correctly | Verify path parameter in request |
Best Practices
- Start Simple: Begin with client-side, add server-side only if needed
- Async Tracking: Don't block application flow for analytics
- Error Handling: Gracefully handle tracking failures
- Test Both Environments: Verify in dev and production
- Monitor Performance: Track impact on page load and server resources
- Respect Privacy: Don't send PII regardless of method
- Use Noscript: Include noscript fallback for client-side
- Document Choice: Record why you chose a particular method
Privacy Considerations
Both methods maintain Simple Analytics' privacy guarantees:
- No cookies used
- No personal data collected
- No cross-session tracking
- No fingerprinting
- GDPR compliant by default
Server-Side Advantage: Even more private as no client-side scripts run at all.
Performance Comparison
| Aspect | Client-Side | Server-Side |
|---|---|---|
| Page Load Impact | Minimal (~2KB script) | None |
| Server Load | None | Minimal per request |
| Setup Time | < 5 minutes | 30-60 minutes |
| Maintenance | None | Update with code changes |
| Ad Blocker Resistance | Low | High |
| Accuracy | 80-95% | ~100% |