Installing the qm.js script

The simplest way to measure a site: a script under 4 KB compressed that writes no identifier on your visitors' devices, neither in a cookie nor in localStorage. Only two things can ever be written, and neither tells one person from another: the qm_ignore opt-out marker, which the visitor sets themselves to stop being counted, and the qm_visit visit continuity cookie, lasting ten sliding minutes. That absence of an identifying or tracking cookie does not by itself guarantee a consent exemption: load the script according to your documented purposes, enabled features and legal basis.

Quick start

1. Get your site key. In your dashboard, open Sites → your site → Installation. Your public key looks like qm_pub_XXXXXXXXXXXXXXXXX. It is visible in the HTML, which is expected; the secret key, however, must never appear on the browser side.

2. Add the script. Copy the qm.js file provided on the installation screen to the root of your site. Served from your domain, tracking stays first-party and domain-based blocklists have no effect:

<script defer src="/qm.js" data-site="qm_pub_XXXXXXXXXXXXXXXXX"></script>

The script derives its collection endpoint from its own origin (…/qm.js…/collect). When served from your domain, forward the hits with the first-party proxy; otherwise, load it directly from the platform:

<script defer src="https://quietmetrics.dev/qm.js"
        data-site="qm_pub_XXXXXXXXXXXXXXXXX"
        data-endpoint="https://quietmetrics.dev/api/v1/collect"></script>

3. Verify. The installation screen checks live for the first hit received. Visit your site: the checkmark turns green.

Script options

Attribute Role Default
data-site Public site key (required: without it, the script does nothing) none
data-endpoint Explicit collection endpoint derived from the script's origin
data-spa SPA navigation tracking (pushState) true
data-outbound Tracking of clicks to external domains enabled ("false" to turn off)
data-downloads “Download” event when a file link is clicked off ("true" to enable)
data-404 “404” event with the requested path, to put on the error template off ("true" to enable)

Single-page applications (SPA)

By default, every history.pushState() triggers a page view: React Router, Vue Router, Inertia, Livewire… work without any extra setup. Two details:

  • A navigation to the same URL does not emit a duplicate.
  • The script guards against double inclusion (window.__qmLoaded): if both your bundler and your layout inject it, only one counts.

To trigger manually (exotic router, hash-based navigation): disable auto-tracking (data-spa="false") and call qm.pageview() after each navigation.

Custom events

<script>
  qm('signup', { plan: 'pro', source: 'landing' });
</script>

Rules: name ≤ 120 characters, ≤ 30 scalar properties (string, number, boolean; no nested objects), values truncated at 190 characters. Avoid high-cardinality values (user ID, email): they are personal data and they saturate the aggregates.

If the script has not loaded yet, queue safely:

<script>window.qm = window.qm || function () { (window.qm.q = window.qm.q || []).push(arguments); };</script>

To count a conversion, create the matching goal in Sites → Goals: an event goal matches the exact name; a page goal matches a path with wildcards (/thank-you, /docs/*).

Without JavaScript: the pixel

For contexts without JS (emails, AMP, <noscript>), a 1×1 GIF counts a page view:

<noscript><img src="https://quietmetrics.dev/p.gif?k=qm_pub_XXXXXXXXXXXXXXXXX&u=https%3A%2F%2Fmysite.com%2Fpage" alt=""></noscript>

The pixel always responds 200 (never a broken image); the actual status is carried in the X-QM-Status header.

Opting out: the exclusion marker

Anyone can ask to stop being counted on your site, with no account and without writing to anyone: they load any URL of the site with ?qm_ignore=1, and ?qm_ignore=0 puts them back into measurement.

https://mysite.com/?qm_ignore=1     stop being counted
https://mysite.com/?qm_ignore=0     be counted again

The marker is a first-party cookie of your own site, named qm_ignore with the value 1 (path=/, samesite=lax, secure over https, five years), which qm.js also mirrors in localStorage. Two stores rather than one: the cookie is the only one of the two a server-side SDK can read, and localStorage takes over wherever the cookie is refused or cleared. A single visit therefore covers both the script and server-side tracking.

It holds no identifier, its value being the same for everyone, it is never transmitted to Quiet Metrics, and it exists only to stop measurement: it is an opt-out marker, not a tracker. The visit that stores it is not counted; the visit that removes it is counted right away.

It is also the simplest way to exclude your own visits, from any browser. The other integrations honour the same marker under the same name: PHP, Laravel, Symfony and the WordPress plugin.

The second, and last, thing written on your visitors' devices. It is not to be confused with the previous one: that one is requested by the person, this one is set by the measurement.

It is called qm_visit, its value is 1, and it is a first-party cookie of your own site (path=/, samesite=lax, secure over https) whose expiry slides by ten minutes and is refreshed on every hit. No localStorage counterpart, unlike the opt-out marker.

What it corrects: the fingerprint that tells your visitors apart is recomputed on every hit from the subscriber network and the browser. When that network changes during a visit, typically when moving from mobile data to wifi, the fingerprint changes with it and the same person counts as two unique visitors in your dashboard. The cookie carries the missing piece: a visit is already under way in this browser.

It holds no identifier, its value being the same for everyone; only its presence is reported, as the c key of the collection payload. And it is never set on a visitor carrying qm_ignore: the opt-out marker stops the measurement before any write.

Serving the measurement rather than the recording of a refusal, this cookie does not benefit from the exemption specific to the opt-out marker: treat it like the rest of your measurement configuration, in the light of your purposes and legal basis.

Special cases

Content-Security-Policy. Local copy: script-src 'self' is enough, plus connect-src 'self' if the first-party proxy is on the same domain. Loading directly from the platform: add https://quietmetrics.dev to script-src and connect-src.

Cache and CDN. The local copy of qm.js can be cached aggressively without risk: the file is stable. After a tracker update, re-download the copy from the installation screen (append ?v=2 to the script URL if your CDN ignores invalidations).

Multiple domains. A site can declare additional domains (Sites → Domains). The log origin policy (default) accepts all hosts while logging them; strict rejects (403) hits whose URL does not match the primary domain, a declared domain, or their subdomains.

Excluding your own visits. Three mechanisms: the opt-out marker above (one visit to ?qm_ignore=1, from any browser), owner self-exclusion in the dashboard (your browser is recognized), or window.__qmDisable = true set by your environment (extension, staging, console).

Test environments. Create a separate site with its own key for staging, or exclude its paths: do not pollute your production statistics.

Hot-path limits

The collection endpoint protects the platform: payload ≤ 4 KB (400 beyond that), 60 hits/minute per IP and per site (429 beyond that), unknown key or rejected origin → 403. All of these responses are invisible to your visitors: analytics never breaks the page.

Visitors without JavaScript can also be counted server-side: see the PHP SDK, Laravel, Symfony or the WordPress plugin.

Can't find your answer?

Support replies on business days.