Privacy-Focused Analytics Tools

Privacy-Focused Analytics Tools

Traditional analytics tools like Google Analytics collect extensive user data by default. Privacy-focused alternatives provide valuable insights while respecting user privacy. These tools use techniques like cookieless tracking, on-premise deployment options, and privacy-preserving aggregation to deliver analytics without compromising individual privacy.

Plausible Analytics offers lightweight, cookie-free analytics that doesn't require consent under GDPR. It provides essential metrics like page views, sources, and geographic distribution without tracking individuals. Matomo (formerly Piwik) provides more comprehensive analytics with privacy controls, including on-premise deployment for complete data control.

// Implementing privacy-focused analytics with Plausible
class PrivacyAnalytics {
  constructor() {
    this.plausibleDomain = 'your-domain.com';
    this.apiKey = process.env.PLAUSIBLE_API_KEY;
  }
  
  // Client-side tracking (no cookies, no PII)
  initializeClient() {
    const script = document.createElement('script');
    script.defer = true;
    script.dataset.domain = this.plausibleDomain;
    // Enable custom events
    script.dataset.api = '/api/event';
    script.src = 'https://plausible.io/js/script.js';
    document.head.appendChild(script);
    
    // Make Plausible available
    window.plausible = window.plausible || function() { 
      (window.plausible.q = window.plausible.q || []).push(arguments);
    };
  }
  
  // Track custom events (privacy-preserving)
  trackEvent(eventName, options = {}) {
    if (typeof window.plausible !== 'function') return;
    
    // Sanitize options to ensure no PII
    const sanitizedOptions = this.sanitizeEventOptions(options);
    
    window.plausible(eventName, { props: sanitizedOptions });
  }
  
  sanitizeEventOptions(options) {
    const sanitized = {};
    const allowedKeys = ['category', 'action', 'value', 'path'];
    
    for (const [key, value] of Object.entries(options)) {
      if (allowedKeys.includes(key) && !this.containsPII(value)) {
        sanitized[key] = value;
      }
    }
    
    return sanitized;
  }
  
  containsPII(value) {
    if (typeof value !== 'string') return false;
    
    // Check for common PII patterns
    const piiPatterns = [
      /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/, // Email
      /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/, // Phone
      /\b\d{3}-\d{2}-\d{4}\b/, // SSN
      /\b(?:\d{4}[-\s]?){3}\d{4}\b/ // Credit card
    ];
    
    return piiPatterns.some(pattern => pattern.test(value));
  }
  
  // Server-side API usage
  async getAggregatedStats(timeframe = '30d') {
    const response = await fetch(
      `https://plausible.io/api/v1/stats/aggregate?site_id=${this.plausibleDomain}&period=${timeframe}`,
      {
        headers: {
          'Authorization': `Bearer ${this.apiKey}`
        }
      }
    );
    
    return response.json();
  }
}

// Alternative: Self-hosted Matomo implementation
class MatomoPrivacyAnalytics {
  constructor() {
    this.matomoUrl = 'https://analytics.yourdomain.com/';
    this.siteId = 1;
  }
  
  initialize() {
    window._paq = window._paq || [];
    
    // Privacy-focused configuration
    _paq.push(['disableCookies']); // No cookies
    _paq.push(['setDoNotTrack', true]); // Respect DNT
    _paq.push(['requireConsent']); // Require consent
    _paq.push(['setSecureCookie', true]); // Secure cookies only
    
    // Anonymize data
    _paq.push(['setIpTracking', false]); // Don't track IP
    _paq.push(['setUserId', false]); // No user ID
    _paq.push(['setVisitorId', false]); // No visitor ID
    
    // Load Matomo
    (function() {
      _paq.push(['setTrackerUrl', this.matomoUrl + 'matomo.php']);
      _paq.push(['setSiteId', this.siteId]);
      const d = document, g = d.createElement('script'), 
            s = d.getElementsByTagName('script')[0];
      g.type = 'text/javascript'; 
      g.async = true; 
      g.src = this.matomoUrl + 'matomo.js';
      s.parentNode.insertBefore(g, s);
    })();
  }
  
  // Grant consent when user approves
  grantConsent() {
    _paq.push(['setConsentGiven']);
  }
  
  // Revoke consent
  revokeConsent() {
    _paq.push(['forgetConsentGiven']);
  }
}