Install the tracker
One line of JavaScript, pasted before the closing body tag, on every page. It is the same line on every website — there is no site key and nothing to fill in. This page is the reference version: the platforms, the byte figures, the failure modes, and what the snippet does and does not touch on the page it sits on.
The line
Paste immediately before </body>, on every page
<script async src="https://tracker.frontlatch.com/t.js"></script>
INSTALL_LINE in apps/web/components/install/content.ts, which matches installTag() in packages/tracker/src/snippet.ts. This is the line /install serves and there is no second version of it.- It is byte-for-byte identical on every site. There is no site ID, no account key and nothing to fill in — a site is recognised by its own domain name. If anybody ever hands you a version of this line with a code or a number in it, it did not come from us.
- Where it must not go: not in the
<head>, not inside a form, and not into a page-builder text widget, which will escape the code and print it on the page as visible words instead of running it. - There is no account to create and nothing to buy. Removing it is deleting the line.
Seven platforms
WordPress leads because it is the most likely answer: of the fifteen Sydney service-business sites scanned on 25 August 2026, nine run WordPress — seven say so outright and two give themselves away with WordPress-only plugins. One runs Wix. The other five do not announce what they are. Source: packages/scanner/benchmark/reports/.
| Platform | Route |
|---|---|
| WordPress | Plugins → Add New → a "headers and footers" plugin (WPCode, Insert Headers and Footers) → its Footer or Body (end) box. Use a plugin rather than editing theme files: a theme update wipes a direct edit and the tracker silently stops with nobody noticing. If your theme already has a Footer scripts or Custom code box, use that and skip the plugin. |
| WordPress with Elementor | Elementor → Site Settings → Custom Code → Add. Location: Body – End. Publish. |
| Squarespace | Settings → Advanced → Code Injection → the Footer box, not the Header box. Save. |
| Wix | Settings → Custom Code → Add Custom Code. All pages. Load code once per page — not "once per visit", because every pageview should be counted. Place code in: Body – end. |
| Shopify | Online Store → Themes → ⋯ beside the live theme → Edit code → theme.liquid in Layout. Paste on the line just above </body>. |
| Google Tag Manager | New Tag → Custom HTML → paste → Triggering: All Pages → Save → **Submit**. A container does not go live until you Submit, and that is the step everybody forgets. If the site already uses GTM this is the fastest route of the lot. |
| Plain HTML | Just above </body> on every page. If there is a shared footer include — footer.php, footer.html, _footer.inc — put it there once and every page picks it up. |
Confirming it works
- The quick check. Load any page, view source, search for
t.js. If the line is there, it is installed. - The real check. Open developer tools, click Network, reload.
t.jsloads with status 200. Now navigate away or close the tab: a request to/bfires and returns 204. That 204 is the collector saying "received" — it always answers 204 whatever it decides about a beacon, because the response is seen by a stranger’s browser on somebody else’s site. - The lazy check, and honestly the best one. Tell us the domain and load a couple of pages. The site appears at our end within a minute and we confirm back.
| Cause | What to do |
|---|---|
| Global Privacy Control or Do Not Track in your own browser | The big one, above. Your own visit is invisible by design. |
| An ad blocker in your own browser | Try a private window, or a different browser. |
| A caching plugin still serving the old copy of the page | Clear the cache. WordPress caching plugins are the usual culprit. |
| A Tag Manager container saved but never submitted | Saving a tag is not publishing it. Open the container and press Submit. |
The line went into the <head>, or into a text widget | In the head it can still work. A page-builder text widget will print the code on the page as visible words. If you can see the line on your site, that is what happened. |
What it costs the page
| Item | Value |
|---|---|
| File size | 5,686 bytes minified · 2,308 gzipped · 2,014 brotli — 2.3 KB on the wire |
| Loading | async — the browser never waits for it, so it cannot delay the page appearing |
| Requests | One per pageview, sent as the visitor leaves the page or after 20 seconds, whichever comes first. Never during loading. |
| Typical request size | About 200 bytes; about 600 bytes on a pageview with a filled six-field form |
| Listeners | All registered passive — the browser’s own guarantee that a script cannot make scrolling stutter |
| Changes to your page | None. No markup added, no styles changed, no redirects, nothing done to your forms. |
The package asserts a budget rather than a measurement, because the measurement moves with the endpoint. The budget is 5,800 bytes and the suite fails if a build breaches it; at the 35-character endpoint the tests use, the built file is 5,689 bytes. Source: packages/tracker/src/snippet.ts and the test run.
The standing rule when the budget binds is written down in the package: cut collection the classifier does not use before touching the budget, and if the only candidates left are signals the classifier does use, raise the budget and record why. That rule has been exercised twice and both moves are on the record — once to restore a detection probe that a byte ceiling had forced out, and once to buy three privacy fixes at +573 bytes with no signal dropped.
The whole snippet is wrapped so that if it ever hit a bug it stops quietly and the page carries on as though it were not there. That is not a promise on trust: the suite runs the actual built file against a stub page and fails the build if it throws.
Removing it
Delete the line. That is the entire removal. There is nothing else to undo: no cookie to clear, no account to close, no plugin left behind, no file on any visitor’s computer. Do it at 2am without telling anybody and nothing breaks at either end.
If you want the visit counts already collected deleted as well, say so and they are deleted — though there is nothing in them that names anybody. Everything is deleted automatically after 60 days regardless, and nothing survives that, not even an aggregate.
Building the snippet yourself
The tag is produced by one function, and the file it points at is built from the classifier and the collection code with the endpoint compiled in.
installTag()
export function installTag(scriptUrl: string): string {
return '<script async src="' + scriptUrl + '"></script>';
}packages/tracker/src/snippet.ts.Building it
npm run snippet -- --endpoint https://<your-collector-host>/b --out dist/t.jspackages/tracker/src/cli.ts prints.Written from
- The tag builder and the byte budget
packages/tracker/src/snippet.ts - The build command
packages/tracker/src/cli.ts - The package explainer
packages/tracker/README.md - The owner-facing install page
apps/web/components/install/content.ts