Features · Analytics
Traffic, without a tracker.
Views, top pages and top sources for each app, counted on Tofu’s own endpoint. No cookie, no third-party script, no stored IP address — and no analytics account to open.
Count visits to corner-bakery.
tofu · siteAnalytics
site key tfsk_…a41c issued · snippet ready
Update(app/layout.tsx)
1 line added · collect.js, no cookie
tofu · ingest ~/corner-bakery
code updated · settings kept
tofu · deploy
ready · HTTP 200 · publicly reachable
One line, or none at all.
Each app has one site key and one line. The key names your app and the addresses allowed to report for it: your Tofu address and the domains you connected, along with any other address on Tofu’s own domains.
In a framework app, your agent adds the line to your layout and deploys. For a prebuilt static site, one switch has Tofu add it at deploy time — to the copy being published, never to your stored files — and the Analytics page says whenever it did not.
<head>
<script defer
src="https://trytofu.ai/collect.js"
data-site="tfsk_…"></script>
</head>Your visitors’ privacy, by construction.
Both ends are Tofu’s: a small script from its own domain, and its own endpoint. Nothing about a visitor leaves Tofu’s infrastructure.
Cookies
0
Nothing stored in the browser
So there is nothing to clear, and no banner to show for one.
Third-party scripts
0
One script, from Tofu’s own domain
Posting to Tofu’s own endpoint. No vendor on your page.
IP addresses stored
0
Used in memory, never kept
Unique visitors, where counted, come from a digest that changes every day, so no one is followed from one day to the next.
Days kept
90
Then deleted, by a daily job
Never kept past 365 days, and deleting the app deletes its traffic with it.
Do Not Track and GPC, honoured twice
Checked in the browser and again on the server: with either signal on, the view is not recorded at all.
Paths, not full addresses
Query strings and fragments are dropped first, and id-shaped segments become :id, so a search term or a reset token in a URL is never stored.
Not tied to anyone’s account
A view is never linked to a signed-in user, and the site key names your app without being able to read or change anything.
Your numbers, in your dashboard and your agent.
| Reported | What it is |
|---|---|
| Views | Every counted page load, per UTC day, with quiet days kept rather than dropped. |
| Top 10 paths | Paths only, with id-shaped segments folded into :id. |
| Top 10 sources | The referring site’s hostname, never its path. |
| Visitors | A count only where Tofu’s visitor salt is configured; otherwise shown as —, never a guess. |
Read it on the Analytics page, or ask your agent: siteAnalytics takes any window from 1 to 90 days. A number that cannot be read comes back as unavailable, never as a zero.
How many people saw the order page this week?
tofu · siteAnalytics days: 7
1,284 views · 7 days
top paths · /order 412 · / 388
top sources · google.com 214 · instagram.com 96
The order page had 412 views this week — counted as views, not people. Most visits came from Google.
By design.
One view per page load. In-app navigation in a single-page app is not a new view, so such an app under-counts.
Crawlers count like people. Bots are not classified: a crawler that sends no Do Not Track signal is a view.
No export and no webhook. Read the numbers in your dashboard or through your agent; there is no CSV and no webhook.
A collector with brakes. Views over the collector's per-minute ceilings are dropped, so a flood or a very large spike can under-count — and your page is never slowed down.
Automatic adding is for prebuilt static sites. A framework app, Next.js included, carries the line in its own layout; the switch adds nothing to it, and the Analytics page says so.
No engagement or Web Vitals. Bounce, time on page and loading metrics are not measured.
Paste one message. Online in 10 minutes.
Your app, its database, your domain and your sign-in — from the agent you already use.