Skip to content

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.

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.

<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>
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.

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");
  1. DevTools → Network → filter t.js. Expect 200 and roughly 5 KB.

    • 404 — the src is 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.
  2. Expect 200 with {"m":"hash"}.

    • 404 {"error":"unknown site"}data-site is 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-api at something that is not the collector.
  3. Expect 202 with {"m":"hash"}.

    Status Meaning
    202 with body Accepted and written
    202 empty Accepted and dropped: excluded IP, bot user agent, or excluded path
    400 bad payload Malformed JSON, or missing s/u
    400 bad url The page URL would not parse, or has no hostname
    404 Unknown site key
    413 Body 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 without tracing_debug, and whether the path matches a data-exclude glob.

    • 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/Sydney and 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.
  4. 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.

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,
);