Help Center / Privacy and GDPR
08 · Privacy and GDPR
Cookieless is an easy claim to make and a hard one to verify. This page is the verification: what Measr puts in the browser, what it writes to your database, everything it deliberately never collects, and how the GDPR obligations you carry map onto infrastructure you own.
Nothing about the visitor. As of v1.3.0 the Measr SDK sets no cookie and keeps no visitor state in localStorage or sessionStorage — no identifier, no session object, nothing to clear. On load it reads exactly one localStorage key, the developer debug toggle below, which only you ever write, while testing. Events are sent to an ingestion endpoint in your own Supabase project, and everything the browser used to remember is derived server-side (see "How sessions are counted" below). The single localStorage key the SDK can touch is a developer debug toggle that you set by hand while testing your integration; it is never written for a visitor and carries no data about anyone.
| Key | Contents | Lifetime |
|---|---|---|
Debug flagmeasr_analytics_debug | Set only if you enable debug mode while testing your integration. Developer-only; never set for a visitor. See the debugging guide for how to use it. | Until you disable it. |
That is the complete list — and the visitor-facing part of it is empty. There is no client-side fingerprinting: no canvas probing, no font enumeration, no hardware signals. The one derived signal is the daily server-side visitor key described below. There is nothing on the device for a visitor to clear, because nothing was ever put there.
Older analytics tools stitch a visitor's page views into a session by leaving an identifier on the device. Measr does it on the server instead. When an event arrives, the ingestion function derives a daily visitor key: a one-way hash of the request's IP address and browser signature together with a secret that changes every day — hash(daily secret + site + IP + browser). The key is used for exactly two things: finding the visitor's live session (a session ends after 30 minutes of inactivity) and counting daily unique visitors.
This is the same architecture privacy-focused tools like Plausible and Fathom run without consent banners. Whether your specific deployment needs one remains a judgment for you or your counsel — this page is a precise technical description, not legal advice.
All analytics rows land in three tables in your own Supabase project: events, sessions, and video events. This is the field inventory; the authoritative reference is the migration files shipped in the repo, which are short enough to read in full. Two small operational tables sit alongside them: the current day's random salt (deleted on rotation) and abuse rate-limit counters — day-scoped hashed buckets on the current ingestion path, while the legacy direct-write path still keys its counters by raw IP inside 60-second windows until it is retired.
| Field | What it contains |
|---|---|
| Visitor key, session ID | The daily visitor key described above (a one-way hash shaped like a UUID) and a random session ID, plus a generated row ID per event. The key changes at each UTC day boundary; each day's values stay in your rows as unlinkable per-day pseudonyms. |
| Site and event type | Your site identifier and which kind of event fired (page view, interaction, video, download, and so on). |
| Page names | The page title (or your data-measr-page-name override), capped at 100 characters, plus the previous page within the visit and the session's entry and exit pages. Names, not full URLs. |
| Referrer | The referring website, or Direct when there is none. |
| UTM parameters | Exactly the four UTM keys the dashboard reports — utm_source, utm_medium, utm_campaign, utm_content (the fourth added in v1.4.0) — captured once when the SDK initializes on a page load. Every other query parameter — emails, tokens, form prefills, and utm_term — is dropped in the browser and never transmitted. Nothing sits in your database that no report reads. Set queryParams: false to capture none at all. |
| Region and language | A continent-level region derived from the browser timezone (Europe, North America, Asia, and so on) and the browser language. No IP-based geolocation, no city, no coordinates. |
| Device category | Desktop, mobile, or tablet, derived from the user agent. |
| Browser and operating system | The browser and OS name (Chrome, Firefox, macOS, Windows, and so on), derived in the browser from the user agent. The user agent string itself is never stored. |
| Event data | What happened: which element was clicked (by its label or name), which outbound link was followed (its destination, stripped of any query string and fragment before transmission), which video reached which milestone and how many seconds in, which file was downloaded, how deep the visitor scrolled, which form was started or completed. Auto-tracking records element names, labels, and URLs, not what a visitor typed. The manual tracking APIs accept whatever you pass them, including free-text names and custom event-data objects, so keep personal data out of anything you send. |
| Timestamps and counters | When the session started and ended, how long it lasted, how many pages it contained (the stored event counter advances with page views), whether it bounced. |
trackError() call are sent as you provide them, so apply the same care there.Bot filtering is on by default (and configurable via excludeBots): automated browsers that expose the webdriver flag and clients matching a list of known crawler user agents are detected at init and write no rows. Detection is pattern-based, so a bot that fully impersonates a regular browser can slip through, which is true of every analytics tool.
Most analytics compliance work is about the vendor: where they process, what their DPA covers, which subprocessors they use, what happens to the data when you leave. Measr removes the analytics vendor from that picture. The SDK on your pages writes directly to your own Supabase project: Creative Data Engineers never receives, stores, or can access your analytics data. One infrastructure party remains, and it is one you already have: your database provider hosts the data as your processor under your existing agreement with them, exactly as it does for the rest of your application. And since visitor UUIDs and captured URLs can still qualify as pseudonymous personal data under GDPR, the controller obligations, lawful basis, and retention decisions stay yours to assess. Measr's design makes that assessment short, not unnecessary.
| Obligation | With a hosted vendor | With Measr |
|---|---|---|
| Data residency | Vendor's regions, vendor's terms | You pick the Supabase region (EU and UK regions available); residency and backups then follow your provider agreement |
| Processor agreement | DPA with the vendor plus their subprocessor list | Your existing agreement with your database provider; no separate analytics processor is added |
| Erasure requests | Ticket to the vendor, their timeline | A DELETE statement on your own tables, on your timeline |
| Access requests | Vendor export tooling | A SELECT on your own tables |
| Retention | Vendor-defined windows | You define retention on tables you control, within your provider agreement |
In practice, erasure and access requests are often short: visitors are day-bounded deterministic hashed keys formatted as UUIDs, so the question is usually whether any record can be attributed to the person asking at all. That assessment is still yours to make for each request, but you make it on data you can fully query, on your own timeline.
Two things a careful reader should know, because precision beats marketing:
utm_source, utm_medium, utm_campaign, and utm_content: every query parameter outside those four keys — an email in a booking link, a reset token, and utm_term (where campaign tools tend to put per-recipient values) — is dropped before transmission. The values of the four captured keys, though, are stored as-is for attribution: if a tool of yours writes personal data into them, that lands in your database. Use utm_content for per-variant values, never per-recipient identifiers; keep personal data out of UTM values, or set queryParams: false.Comparing tools on privacy grounds? The privacy analytics comparison puts Measr, Plausible, Fathom, Matomo, Simple Analytics, and Umami side by side on exactly these criteria.
localStorage holds no identifier and no session object. Sessions are stitched server-side from a daily-rotating hashed key. The one localStorage key the SDK reads is a developer debug toggle that only you ever write, while testing your integration.DELETE statement on tables you own. Visitors are day-bounded hashed keys with no name or email attached by default, so the practical question is usually whether any record can be attributed to the person asking at all. That assessment is yours to make, on data you can fully query.