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>
/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.
- 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.
- 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.
- 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.
| 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.
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.
| 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.
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>';
}Building it
npm run snippet -- --endpoint https://<your-collector-host>/b --out dist/t.jsBuilding 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.