Skip to content
whovisited.

Implementation

Why a website analytics tracker splits visits across subdomains

Learn why the same browser can appear as separate visitors on your main site and app subdomain, how allowed origins affect collection, and what to check before comparing those journeys.

A shared domain does not mean a shared browser origin

A visitor reads your marketing site at `www.example.com`, clicks Sign in, and continues at `app.example.com`. The business sees one journey. An analytics report may show two anonymous visitors, two sessions, and no referral between them. That can be expected behavior rather than a broken installation.

Browsers define an origin by scheme, host, and port. `https://www.example.com` and `https://app.example.com` have different hosts, so they are different origins even though both names end in `example.com`. Browser storage is separated by origin for security; JavaScript on one host cannot read the other host's local or session storage.

This distinction matters for analytics because many trackers keep an anonymous browser identifier in localStorage and a session identifier in sessionStorage. When the browser changes origin, the destination page cannot read the identifiers stored by the first host unless the system has an explicit cross-origin identity design.

For example, `https://example.com/pricing` and `https://example.com/signup` are the same origin because only the path changes. `https://www.example.com/pricing` and `https://example.com/signup` are different origins, as are `https://example.com` and `https://example.com:8443`. A redirect that changes the hostname can therefore change the browser's storage boundary even when the address still looks like part of the same product.

What happens with the WhoVisited tracker

WhoVisited creates a random visitor ID and a session ID in the browser. The storage keys include the site ID, but the browser still keeps storage separate for each origin. Installing the same site snippet on `www.example.com` and `app.example.com` therefore does not make those hosts share a visitor ID or session ID. The server also validates the event URL against the origin that requested its short-lived ingestion token.

Allowed origins decide which websites may send data for a site. They are an origin allowlist, not a setting for joining identities. Add each host that should collect data, for example `https://example.com` and `https://app.example.com`, and install the same site snippet on those pages. The configured HTTPS main domain also covers its `www` version; other subdomains need their own entry.

html
<script async src="https://whovisited.net/tracker.js" data-site-id="YOUR_SITE_ID"></script>

Use the exact scheme and hostname that visitors use. An HTTP development host, a preview deployment hostname, and a custom port are separate origins. Only allow origins you control; adding a hostname permits its pages to submit events under this site's configuration, but it does not merge browser storage.

Why the handoff may not look like a referral

Browsers do not automatically share the first host's session with the second. In addition, WhoVisited intentionally removes referrers from the same site’s other allowed origins when it builds an external referral source. A hop from `www.example.com` to `app.example.com` is therefore not treated as an outside website referring traffic. If the app starts a fresh session without campaign parameters, that session may have no source label.

If campaign context needs to be available on the destination host, preserve only the campaign parameters you actually use, such as `utm_source`, `utm_medium`, and `utm_campaign`, through your own redirect or link flow. WhoVisited accepts those three values when they pass its validation rules. Avoid putting email addresses, account IDs, session tokens, or other personal data in a URL; URLs can be copied, logged, and sent as referrers to other services.

Campaign labels can help describe the new session, but they are not a cross-domain visitor key. Do not copy a browser ID into a query string to force a match. That creates a new data-sharing mechanism with security and privacy consequences, and it is not a supported WhoVisited identity feature.

Verify each host before reading the report

  1. List the actual page origins used in the journey, including `www`, `app`, or `checkout` hosts. Compare scheme, hostname, and port, not just the registrable domain.
  2. In WhoVisited Settings, add each intended production origin to the site's Allowed origins. Install the same site ID's snippet on every host that should collect activity.
  3. Open the site in a clean browser profile with the Network panel recording, then load one page on each host. Filter for `bootstrap` and `events`; check that the bootstrap and event requests return successfully and that the page is not blocked by consent, an extension, or a content security policy.
  4. In the browser's Application storage view, inspect each host separately. The `wv:visitor:<site-id>` and `wv:session:<site-id>` entries are scoped to that origin; expect separate values across subdomains. Do not paste those identifiers into tickets, analytics properties, or URLs.
  5. Open the visitor report after test events arrive. Treat the hosts as separate anonymous browser records and use campaign labels only when they were deliberately carried to the new landing page.

Chrome DevTools' Network panel records requests while it is open and lets you inspect their status, headers, and response. A successful script download alone is not proof that tracking worked: confirm the bootstrap and event requests too, then confirm the resulting activity in WhoVisited. A browser request can succeed while the business action you care about still fails, so emit outcome events only from a success path you trust.

If the snippet loads but no requests appear, check whether collection is waiting for consent or a browser privacy signal is disabling it; do not bypass a visitor's consent choice just to make a test pass. If bootstrap fails, check that the page's exact origin is allowed and the site is enabled. If the event request is rejected, inspect its response before changing the snippet. A successful request also does not certify that a signup, payment, or other server-side business outcome really happened.

Choose a reporting boundary that matches your product

If the main question is which pages lead to a signup, keeping that flow on one canonical host and using paths such as `/signup` and `/welcome` gives the tracker one origin and a simpler browser journey. If your application must live on a subdomain, collect both hosts under the intended site and accept that WhoVisited currently records origin-scoped anonymous identifiers rather than stitching the same person across them.

Do not interpret two visitor rows as two people, or a missing subdomain referral as proof that a campaign failed. Check the host, the browser storage boundary, the event requests, and any intentionally preserved UTM values first. The [installation guide](https://whovisited.net/guides/install-website-analytics) explains origin setup, and the [source and conversion guide](https://whovisited.net/guides/traffic-source-conversion-tracking) explains what a session-level source or goal can and cannot establish.

This is a browser-level limit with a practical reporting consequence: configuration can authorize another origin to collect, but it cannot make the browser share origin-scoped storage. Before comparing totals, decide whether your question is about the whole business domain or about activity that the current product can reliably associate with one anonymous browser ID. Keep that limitation visible when sharing the report with teammates.

References and further reading

  1. MDN: Same-origin policy
  2. MDN: Window.localStorage
  3. MDN: Web Storage API
  4. Chrome for Developers: Inspect network activity