Building Web Analytics with open-source tools
This guide outlines the implementation of a privacy-first, cookie-less web analytics strategy. It focuses on bypassing ad-blockers via reverse proxying and instrumenting custom event tracking for product-led growth metrics without requiring user consent banners.
Configure a Reverse Proxy for the Tracking Script
To prevent ad-blockers from stripping your analytics script, proxy the tracking script through your own domain. This ensures the request originates from your first-party domain rather than a known tracking CDN.
{
"rewrites": [
{
"source": "/stats/js/script.js",
"destination": "https://plausible.io/js/script.js"
},
{
"source": "/stats/api/event",
"destination": "https://plausible.io/api/event"
}
]
}⚠ Common Pitfalls
- •Failure to proxy the API endpoint (/api/event) will result in script loading but data failing to send.
- •Ensure your analytics provider supports custom domains or proxying before implementation.
Initialize the Tracking Snippet
Inject the script into your application's head. Use data-attributes to define the domain and ensure the script points to your proxied path rather than the provider's default URL.
<script defer data-domain="yourdomain.com" data-api="/stats/api/event" src="/stats/js/script.js"></script>⚠ Common Pitfalls
- •Setting data-domain incorrectly will lead to a 403 Forbidden error from the analytics collector.
- •Loading the script synchronously will negatively impact Core Web Vitals (LCP).
Implement Custom Event Tracking for UI Interactions
Standard pageview tracking is insufficient for modern apps. Use a global helper to trigger custom events for specific user actions like 'Signup Clicked' or 'AI Prompt Submitted'.
export const trackEvent = (eventName: string, props?: Record<string, string | number | boolean>) => {
if (typeof window !== 'undefined' && (window as any).plausible) {
(window as any).plausible(eventName, { props });
}
};⚠ Common Pitfalls
- •Do not include Personally Identifiable Information (PII) like email addresses in props to maintain GDPR compliance.
- •Ensure the script has finished loading before calling the tracking function, or use a queueing pattern.
Track Server-Side Events for AI Features
Client-side tracking can miss events if a user closes the tab before an AI response finishes. Trigger a server-side POST request to the analytics API when an AI generation completes.
await fetch('https://yourdomain.com/stats/api/event', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'User-Agent': userAgent },
body: JSON.stringify({
name: 'ai_generation_complete',
url: 'https://yourdomain.com/dashboard',
domain: 'yourdomain.com',
props: { model: 'gpt-4', tokens: 450 }
})
});⚠ Common Pitfalls
- •Server-side calls require manual User-Agent and X-Forwarded-For headers to accurately record geographic data.
- •Rate limits on the analytics provider's API can drop server-side events if not handled with a retry logic.
Verify Data Accuracy and Payload Structure
Open the browser's Network tab and filter by the proxied path. Trigger an event and verify the payload is a valid JSON object with the expected properties. Check that no cookies are being set in the Response Headers.
⚠ Common Pitfalls
- •Testing with an active ad-blocker that uses CNAME cloaking detection might still block the proxy; test in a clean environment.
- •Verify that the 'domain' field in the payload matches exactly what is configured in your analytics dashboard.
What you built
You now have a robust, privacy-compliant analytics setup that avoids common ad-blocking pitfalls. By using a reverse proxy and custom event helpers, you can track deep product engagement metrics while maintaining a zero-cookie footprint.