SvelteKit installation
This page has two tiers: the install line that measures browser pageviews, then the app-hook forwarder that observes requests that never run JavaScript.
What you get
Every page load and client-side navigation that changes the pathname is measured as a pageview. The line cannot block rendering, interfere with your router, or set cookies. The forwarder observes requests without blocking, redirecting, or changing responses.
Prerequisites
| You need | Where it comes from |
|---|---|
Your site key (pk_…) |
Onboarding |
Managed-proxy hostname (<label>.<your-domain>) and its CNAME target |
We name the record at onboarding, then confirm when the hostname is active |
Ingest URL and forwarder secret (sk_…) |
Onboarding |
| Node.js runtime with writable persistent storage | Required for the forwarder’s durable queue |
The managed proxy is decided and in build; onboarding is concierge today. For a strict same-origin Content Security Policy, use the rewrite alternative in step 3.
Install steps
1. Add the CNAME record, then wait for confirmation
Create the single CNAME record we name for <label>.<your-domain>. Wait until we tell
you the hostname is active before adding the line.
2. Add the install line to src/app.html
src/app.html is the shell SvelteKit renders every page into, so one edit there
covers your whole app. Add the line inside <head>, above the %sveltekit.head%
placeholder:
<head>
<meta charset="utf-8" />
<script defer src="https://<label>.<your-domain>/px/t.js" data-site="pk_your_site_key"></script>
%sveltekit.head%
</head>
Replace pk_your_site_key with your own site key. Anywhere in the document works —
<head> is simply the one place guaranteed to be in every page of the app. What
matters is the meaning, not the exact bytes: a deferred script sourcing /px/t.js
and carrying your data-site.
Strict Content Security Policy alternative. Keep the same install-line semantics
but use this same-origin form and configure the platform or CDN in front of your site
to rewrite /px/* to the managed proxy:
<script defer src="/px/t.js" data-site="pk_your_site_key"></script>
This is a permanent alternative, not an old form. If you must run the relay yourself, use the Node proxy reference.
This line is frozen. It is stable for the life of your installation; no future version will ask you to edit it.
3. Add the forwarder
npm install @aurascope-analytics/forwarder-app-hook
In src/hooks.server.ts, guard the build and observe every runtime request:
import { building } from "$app/environment";
import type { Handle } from "@sveltejs/kit";
import { createAuraScopeMiddleware } from "@aurascope-analytics/forwarder-app-hook";
const auraScope = createAuraScopeMiddleware();
export const handle: Handle = async ({ event, resolve }) => {
if (!building) auraScope(event.request);
return resolve(event);
};
This tier requires a Node runtime and writable filesystem; it does not run on Edge
adapters. Match every path, or accept that unmatched paths are unobserved. status
ships as null in this version because observation happens before the response.
New crawlers can appear in your history retroactively as registries update. Crawler fetches are requests, not pageviews; AI-referral counts are always at least the displayed number. Unusual setup?
4. Rebuild and deploy
vite build, then deploy through whichever adapter you already use. The line is
plain markup in the shell, so every adapter — static, Node, or a platform adapter —
serves it identically.
Verify your install
-
The line survived the build. From your build output, or against the deployed site:
curl -sS https://www.your-site.example/ | grep '/px/t.js'Expect the line itself back.
-
The script is served.
curl -sS -D - -o /dev/null https://<label>.<your-domain>/px/t.jsExpect
200andcontent-type: text/javascript; charset=utf-8. -
Navigation is measured. Open your app with DevTools → Network. You should see
t.jsreturn200, then one/px/erequest return204on the first load. Then:What you do What you should see Click a link to another route One more e,204Click a link that only changes the query string (a filter, a sort) Nothing — this is deliberate Press the browser back button onto a different path One more e,204Reload One more e,204Click an in-page anchor ( #section) †Nothing — this is deliberate A pageview is a pathname change. Query-string and hash changes are not pageviews, so a faceted list page does not inflate your counts as a visitor refines it.
† Every row above except the last was driven in the end-to-end run this guide is written from. The in-page-anchor row is the one behaviour we assert from the snippet’s rules and its own test suite rather than from that browser run — it holds for the same reason the query-string row does, but say so if you see otherwise.
-
Confirm the forwarder. Log
auraScope.forwarder?.metrics()after traffic:observedclimbs,acceptedcatches up, anddroppedstays0. -
End-to-end confirmation. There is no self-serve install checker yet, so the last step — your test pageviews visible as durably stored rows — comes from us during onboarding. Ask once steps 1–3 pass. This is the only step that catches a misconfigured relay behind your
/px/*routes: your browser still sees204either way, because the measurement path is deliberately fail-open.
Failure modes — and what they do to your site
Nothing on your site waits for AuraScope. The install line is deferred, and the forwarder copies request metadata then queues and ships it off the response path. If the service is unreachable it pauses, retains, and resumes; only capacity exhaustion drops oldest events, and that loss is counted.
Troubleshooting
/px/t.js returns 404
The line is installed but the managed-proxy hostname is not active, or the same-origin rewrite is absent. Check the onboarding confirmation and the platform/CDN rewrite.
The first load is measured, later navigations are not
The script did not load, or loaded from a page that then hard-navigated away. Confirm
t.js returned 200 on the first load, and that your links are ordinary SvelteKit
navigations rather than full document loads (a full load is still measured — it will
just look like a first load every time).
A filter or sort click sends nothing
That is correct behaviour, not a fault. Only pathname changes count.
Two e requests fire on one navigation
The line is in the document twice — commonly once in src/app.html and once added to
a layout component as well. Keep exactly one.
Uninstall
Remove the line from src/app.html, rebuild, and deploy. That is everything: nothing
was installed on your server, and nothing was stored in your visitors’ browsers.