DocsGetting started / Install the tracker

Install the tracker

One line of JavaScript, pasted before the closing body tag, on every page. It is the same line on every website, with nothing to fill in. This page is the reference version.

The line

Paste immediately before </body>, on every page

<script async src="https://tracker.frontlatch.com/t.js"></script>

Select the text above and copy it.

Copied from the same source /install renders its line from, which matches the tracker’s own installTag() builder. 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 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.

Seven platforms, three kinds of door, one landing

Just before the closing body tagThe same line, byte for byte, on every page of every site. Whichever door you come through, this is where it ends up, and nothing else on the page changes.

  1. A code box in the settingsWordPress · Elementor · Squarespace · WixA box the platform provides for code it will add to every page. Use the footer or body-end box, never the header one, and never a text widget, that prints the line on the page as visible words.
  2. The template fileShopify · plain HTMLThe file every page is built from, theme.liquid, or a shared footer include. Paste once, on the line above the closing body tag, and every page picks it up.
  3. A tag in a containerGoogle Tag ManagerA Custom HTML tag, triggered on all pages. Saving is not publishing: the container goes live when you press Submit, and that is the step everybody forgets.
  • Your page and its closing tag: untouched.
  • The line, the one thing added, and its way in.
  • A door: the platform’s own box, not ours.
Seven routes are in the table below; three shapes are drawn. Four platforms give you a code box, two give you the file, one gives you a container. Drawn from that table, not recorded from any site.
Where the line goes
PlatformRoute
WordPressPlugins → 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 ElementorElementor → Site Settings → Custom Code → Add. Location: Body – End. Publish.
SquarespaceSettings → Advanced → Code Injection → the Footer box, not the Header box. Save.
WixSettings → 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.
ShopifyOnline Store → Themes → ⋯ beside the live theme → Edit code → theme.liquid in Layout. Paste on the line just above </body>.
Google Tag ManagerNew 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 HTMLJust 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

  1. The quick check. Load any page, view source, search for t.js. If the line is there, it is installed.
  2. The real check. Open developer tools, click Network, reload. t.js loads with status 200. Now navigate away or close the tab: a request to /b fires 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.
  3. 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.

Brave and the DuckDuckGo browser send Global Privacy Control by default, Firefox sends it if "tell websites not to sell or share my data" is ticked, and some privacy extensions send Do Not Track. When either signal is present the snippet stops before it starts: no listeners, no counting, no request.

That means your own test visit can look like a failure, because you are the visitor who asked not to be counted. Test in Chrome or Safari with no privacy extension, or use the lazy check and let Frontlatch confirm from its end.

If the line is there and nothing arrives
CauseWhat to do
Global Privacy Control or Do Not Track in your own browserThe big one, above. Your own visit is invisible by design.
An ad blocker in your own browserTry a private window, or a different browser.
A caching plugin still serving the old copy of the pageClear the cache. WordPress caching plugins are the usual culprit.
A Tag Manager container saved but never submittedSaving a tag is not publishing it. Open the container and press Submit.
The line went into the <head>, or into a text widgetIn 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

Page cost
ItemValue
File size5,686 bytes minified · 2,308 gzipped · 2,014 brotli, 2.3 KB on the wire
Loadingasync: the browser never waits for it, so it cannot delay the page appearing
RequestsOne per pageview, sent as the visitor leaves the page or after 20 seconds, whichever comes first. Never during loading.
Typical request sizeAbout 200 bytes; about 600 bytes on a pageview with a filled six-field form
ListenersAll registered passive, the browser’s own guarantee that a script cannot make scrolling stutter
Changes to your pageNone. No markup added, no styles changed, no redirects, nothing done to your forms.
Measured on 26 August 2026 against the live endpoint, not estimated. The collector URL is baked into the build, so its length moves these figures by roughly a byte per character. One ordinary photograph on a homepage is forty to a hundred times bigger than this file.

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.

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 test page and fails the build if it throws.

The built snippet is 5,689 bytes, and 2.3 KB over the wire. The extra bytes pay for three privacy protections: password, hidden and file inputs are excluded from field observation, the URL path is cut to its first segment before it leaves the browser, and Global Privacy Control and Do Not Track are honoured.

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>';
}
Verbatim from the tracker’s own snippet builder.

Building it

npm run snippet -- --endpoint https://<your-collector-host>/b --out dist/t.js
Verbatim from the usage line the tracker’s CLI prints.

Building the snippet without --endpoint fails with an error rather than producing something that looks ready to ship; buildSnippet() throws, and localhost is not a fallback.

A default would point at a host someone else could register, and the snippet would then send a business’s visitor telemetry to a stranger. The scanner follows the same rule for its own contact address.

See what an AI agent can do on your site.