Opt-out & debugging
The tracker reads exactly two localStorage keys. It never writes either one —
that is entirely up to you or the visitor.
| Key | Value | Effect |
|---|---|---|
tracing_disabled |
"1" |
Nothing is ever sent from this browser, for any site |
tracing_debug |
"1" |
Hits are sent even from localhost, 127.0.0.1, [::1] and file: |
Both reads are wrapped in try/catch, so a browser with storage blocked
behaves as if neither flag were set.
Opting a visitor out
Section titled “Opting a visitor out”localStorage.setItem("tracing_disabled", "1");localStorage.removeItem("tracing_disabled");The flag is per browser and per origin, and it is checked before every hit —
including pageviews, custom events, outbound clicks, revenue events, identify
calls and the duration flush. The identity probe to /c/:key still runs,
because it is fired before the first send(); it carries no data about the
visitor and sets nothing.
A button on your site
Section titled “A button on your site”<button id="tt-optout">Do not count my visits</button><script> const key = "tracing_disabled"; const button = document.getElementById("tt-optout");
function render() { button.textContent = localStorage.getItem(key) === "1" ? "Analytics off — turn back on" : "Do not count my visits"; }
button.addEventListener("click", () => { if (localStorage.getItem(key) === "1") localStorage.removeItem(key); else localStorage.setItem(key, "1"); render(); });
render();</script>Other ways to exclude traffic
Section titled “Other ways to exclude traffic”| Method | Scope | Where |
|---|---|---|
tracing_disabled |
one browser | the visitor, or your own console |
| Excluded IPs | every browser on that address | Settings → General |
| Excluded paths | whole sections of the site | Settings → General |
data-exclude |
whole sections, client-side | the script tag |
Server-side exclusions are stronger: they apply to traffic that is already live and cannot be bypassed by a visitor. See Bots & exclusions.
Debugging from localhost
Section titled “Debugging from localhost”localStorage.setItem("tracing_debug", "1");Reload. Every hit is now sent as though the page were deployed.
Remember to turn it off:
localStorage.removeItem("tracing_debug");Nothing is arriving
Section titled “Nothing is arriving”-
Is
Section titled “Is /t.js loading?”/t.jsloading?DevTools → Network → filter
t.js. Expect200and roughly 5 KB.404— thesrcis wrong.- Blocked by CSP — add the collector host to
script-src. See CSP. - Blocked by an ad blocker — some lists block any third-party analytics host. Proxy the collector behind your own domain.
-
Is
Section titled “Is /c/:key answering?”/c/:keyanswering?Expect
200with{"m":"hash"}.404 {"error":"unknown site"}—data-siteis not a key on any live site. Check for a typo, or for a key that belonged to a deleted site.- CORS error — you are pointing
data-apiat something that is not the collector.
-
Is
Section titled “Is POST /e being sent?”POST /ebeing sent?Expect
202with{"m":"hash"}.Status Meaning 202with bodyAccepted and written 202emptyAccepted and dropped: excluded IP, bot user agent, or excluded path 400 bad payloadMalformed JSON, or missing s/u400 bad urlThe page URL would not parse, or has no hostname 404Unknown site key 413Body over 32 KiB — almost always a huge property object No request at all means the tracker suppressed it. Check, in this order:
localStorage.tracing_disabled, whether you are on localhost withouttracing_debug, and whether the path matches adata-excludeglob. -
Is the dashboard showing it?
Section titled “Is the dashboard showing it?”- Realtime polls every five seconds and shows the last 30 minutes. Start there, not on Overview.
- Overview is cut in the site’s timezone. If the site is in
Australia/Sydneyand you are in Europe, “Today” may not be your today. See Time ranges & timezones. - A filter chip left over from an earlier click hides everything that does not match. Clear it.
- Your own IP may be in Excluded IPs.
-
Is the browser being classed as a bot?
Section titled “Is the browser being classed as a bot?”The collector parses the user agent and drops anything it classes as a bot with an empty
202. Headless Chrome, Playwright and curl are normally classed that way. Test with a real browser.
Reading the payload yourself
Section titled “Reading the payload yourself”Everything is plain JSON. In DevTools → Network → the /e request → Payload
you will see the hit shape
exactly as sent.
To watch what your own code produces without deploying:
// paste in the console before your app calls tracing()const real = window.tracing;window.tracing = Object.assign( (name, props) => { console.log("event", name, props); real(name, props); }, real,);