Skip to content

Privacy & Opt-Out

Pomelo Analytics is built around a privacy-first, cookieless-by-default approach. This page documents Pomelo’s current collection boundaries and how to give your visitors control.

Doctrine note: Pomelo is moving toward a Strict by default / Extended by configuration model. This page documents the privacy baseline and opt-out controls. It documents boundaries rather than making a legal-status promise for every Pomelo surface.

  • No measurement cookies — Pomelo does not set, read, or require cookies for analytics measurement. The only cookie used is the first-party opt-out cookie when a visitor explicitly objects.
  • No localStorage / sessionStorage — No data is stored on the visitor’s device (except the opt-out objection cookie, see below).
  • No fingerprinting — No canvas, WebGL, font enumeration, or other browser fingerprinting techniques.
  • No cross-site tracking — The daily visitor key is scoped per site and cannot link visitors across different domains.
  • No IP storage — IP addresses are used only transiently for pseudonymization (HMAC) and geolocation, then discarded.
  • No user profiles — Pomelo does not build behavioral profiles or advertising segments.

Pomelo always respects Do Not Track (DNT) and Global Privacy Control (GPC) signals. This is not opt-in — it is enforced unconditionally:

  • Client-side: The tracking script checks navigator.doNotTrack and navigator.globalPrivacyControl. If either is enabled, no events are sent.
  • Server-side: The ingest API checks DNT: 1 and Sec-GPC: 1 headers. Events with these headers are silently dropped as defense-in-depth.

Pomelo provides a JavaScript API for visitors to opt out of measurement:

Stops all tracking immediately:

  • Sets a first-party cookie pomelo_optout=1 (valid 1 year, SameSite=Lax)
  • Sets window.__POMELO_OPTOUT__ = true
  • Clears any pending event queue

Re-enables tracking:

  • Removes the pomelo_optout cookie
  • Deletes the window.__POMELO_OPTOUT__ flag
<!-- Add to your privacy page or footer -->
<button id="pomelo-optout">Opt out of analytics</button>
<button id="pomelo-optin" style="display:none">Re-enable analytics</button>
<script>
document.getElementById('pomelo-optout').addEventListener('click', () => {
window.pomelo.optout(true);
alert('You have opted out of analytics measurement.');
document.getElementById('pomelo-optout').style.display = 'none';
document.getElementById('pomelo-optin').style.display = '';
});
document.getElementById('pomelo-optin').addEventListener('click', () => {
window.pomelo.optout(false);
alert('Analytics measurement has been re-enabled.');
document.getElementById('pomelo-optin').style.display = 'none';
document.getElementById('pomelo-optout').style.display = '';
});
</script>

You can also block tracking before the script loads by setting the global flag:

<script>
window.__POMELO_OPTOUT__ = true;
</script>

The pomelo_optout=1 cookie is a first-party cookie that stores only the visitor’s objection. It is:

  • Not a tracking identifier — it contains no unique value.
  • First-party only — set on your domain, not a third-party domain.
  • SameSite=Lax — not sent in cross-site requests.
  • Valid for 1 year — so the visitor does not need to opt out on every visit.

This cookie is only set when the visitor explicitly opts out. Pomelo does not set any cookies during normal measurement.

Pomelo alone is designed to support a minimized audience-measurement setup only when you stay inside the documented strict envelope.

This does not mean:

  • every Pomelo surface has the same legal reading
  • Extended can be treated as a consent-light default
  • your whole site can remove its banner without reviewing the rest of its trackers

If the page also loads ad tech, personalization tags, session replay, or any other consent-requiring tracker, the site can still require consent UI independently of Pomelo.

Data pointStored?Details
IP addressNoUsed transiently for HMAC + geolocation, then discarded
CookiesNoOnly pomelo_optout if the visitor explicitly opts out
User agentServer-side onlyParsed from the HTTP header for device/browser/OS classification; never sent from the browser
Page URLYesCanonicalized URL with hash removed; Strict drops query params, Extended can keep an allowlisted acquisition subset
ReferrerYesStrict keeps host-based referrer reads; full referrer values stay outside the default strict envelope
GeolocationYesCountry can stay in the strict baseline; region/city remain enriched slices
Daily visitor keyYesHMAC, resets at UTC midnight, scoped per site
Session IDPseudonymized daily keyHMAC of domain + day + IP + browser family; no raw UA used

The tracking script applies automatic sanitization to captured text as a defense-in-depth measure against accidental PII leakage.

  • Identifiers (data-analytics-id, data-analytics-scope, form IDs): safe charset only (a-z, 0-9, _, -, ., :, /, space), max 100 characters. Dropped entirely if PII is detected.
  • Labels (data-analytics-label, auto-detected labels): max 80 characters. Dropped entirely if PII is detected.
  • Page titles: max 200 characters. Dropped entirely if PII is detected.
  • Error messages: query strings stripped, PII patterns replaced with [redacted], max 200 characters, and only when enriched error capture is enabled.

PII patterns detected include:

  • Email addresses
  • Phone numbers
  • UUIDs
  • Long numeric sequences (10+ consecutive digits)
  • Token-like strings (20+ alphanumeric characters)

If in doubt, the SDK drops the field instead of sending it.

The SDK silently drops values that look unsafe or overly sensitive, including:

  • Email addresses
  • Phone numbers
  • UUIDs
  • Long numeric sequences
  • Token-like strings
  • Free-form text from form fields
  • Text from contentEditable regions

This is intentional: Pomelo prefers losing a data point over transmitting sensitive data.

See also: SDK Integration for the full attribute reference and sanitization rules.