DEV Community

Cover image for Building a Zero-Cookie Global Visitor Map with Cloudflare Edge & Supabase
Adil Rahiman
Adil Rahiman

Posted on Originally published at adilrahman.cc AI-assisted

Building a Zero-Cookie Global Visitor Map with Cloudflare Edge & Supabase

While traditional platforms like Google Analytics are great for private backend dashboards, powering a public, client-facing visual map in real-time requires a completely different approach.

Here’s how I built a privacy-first, GitHub contribution–style world map on a static Next.js site using Cloudflare edge headers and Supabase atomic RPCs.

(Visual concept inspired by Prakhar's open-source kprakz/homeflix).


The Architecture

  1. Cloudflare Edge Trace: Extracts ISO country code (loc=IN) in-memory via /cdn-cgi/trace and immediately discards the raw IP address (zero cookies, zero IP storage).
  2. Supabase Atomic RPC: Aggregates visits per country using an atomic PostgreSQL function (increment_country) to prevent race conditions during traffic spikes.
  3. Daily Device Lock: Uses localStorage keyed with today’s date (YYYY-MM-DD) so each device logs strictly once per day across all endpoints.
  4. Root Layout Telemetry: Headless component in src/app/layout.js captures visitors landing directly on any endpoint from Google Search.
  5. Real-Time SVG Grid: A 120×56 matrix (~24KB) with Euclidean distance ripple animations (Math.hypot()) and live WebSocket updates via Supabase Realtime.

Telemetry Implementation

'use client';
import { useEffect } from 'react';
import { supabase } from '../lib/supabaseClient';

export default function VisitorTelemetry() {
  useEffect(() => {
    if (typeof window === 'undefined') return;

    // Only record on production domain
    const isProd = ['adilrahman.cc', 'www.adilrahman.cc'].includes(window.location.hostname.toLowerCase());
    if (!isProd) return;

    // 1 visit per device per day
    const today = new Date().toISOString().slice(0, 10);
    if (localStorage.getItem('ar_visit_logged_date') === today) return;

    async function recordVisit() {
      try {
        const res = await fetch('/cdn-cgi/trace');
        const text = await res.text();
        const match = text.match(/loc=([A-Za-z]{2})/);
        if (match && match[1]) {
          const code = match[1].toUpperCase();
          localStorage.setItem('ar_visit_logged_date', today);
          localStorage.setItem('ar_visit_logged_country', code);
          await supabase.rpc('increment_country', { c_code: code });
        }
      } catch (_) {}
    }

    recordVisit();
  }, []);

  return null;
}
Enter fullscreen mode Exit fullscreen mode

Check out the full tutorial with complete PostgreSQL schemas, logarithmic color scales, and SVG centroid ripple math:

👉 https://adilrahman.cc/blog/zero-cookie-visitor-map-cloudflare-supabase

Top comments (0)