Page Groups
StatelessID strips query strings from URLs before counting. You see how many times a path was loaded, not what parameters visitors carried or what they searched for.
Query strings are removed at collection
When asset.js fires the beacon, it reads window.location.pathname. It does not read window.location.search. The query string — the ?key=value portion of the URL — is never assembled into the beacon payload. The count lands on the clean path regardless of what parameters the page was loaded with.
This is not a server-side filter applied after receiving the full URL. The script simply does not include the query string. It never travels from the browser to the server as part of the beacon. There is no intermediate step where the full URL is received and then stripped.
Why query strings are excluded
Query strings often carry information that narrows toward a person: session tokens, affiliate tracking codes, email campaign parameters, search terms with personal intent, and in some systems, user identifiers embedded by a CRM or email platform. Excluding the query string at collection time keeps the stored path data from becoming a partial identity record.
A path like /checkout?email=user%40example.com would record the email address in any system that captures the full URL. With asset.js, that page load is recorded as /checkout. The email address never enters the beacon and is never stored.
How paths appear in the board
The Pages section of the dashboard shows the path as recorded. /product/42 and /product/99 appear as separate rows because they are different pathnames. If your URL structure embeds dynamic IDs, you will see a row per unique path that was loaded during the selected period.
StatelessID does not merge dynamic path segments automatically. There is no wildcard grouping like /product/*. Reading the traffic pattern across a family of paths is done by looking at the prefix they share in the board. That is a deliberate trade-off: automatic path grouping requires rules that must know your URL structure, and keeping the stored path literal means you always see exactly what was loaded.
Hash fragments
The hash portion of a URL — the #section part — is a client-side construct. It is never included in HTTP requests, so it never arrives at any server. asset.js does not need to strip it because it never existed in the network layer to begin with.
If your application uses hash-based client-side routing, each hash navigation does not trigger a new page load unless the framework performs a full document navigation. A single page load that changes the hash several times produces one beacon, not one per hash change.
Troubleshooting
If you see many distinct paths like /product/1, /product/2, /product/3 instead of a single grouped entry, that is correct behavior. StatelessID records what window.location.pathname reported. Reading those rows as a group — by their shared /product/ prefix — is an interpretation you apply in how you read the board.
If a path appears in the board with what looks like query string content, confirm that asset.js is not running on a page where another script has rewritten window.location before the beacon fires. A framework that reflects parameters into the pathname (not the query string) would cause the path itself to vary. The beacon reads whatever pathname was set at DOMContentLoaded.