Technical
Hash routing
Also called hashbang.
Hash routing is client-side navigation that keeps the view in the URL fragment after `#`, as in `/app#/settings`. Browsers never send the fragment to the server, so the server only ever sees `/app`.
How it is measured
Watch `location.hash` in the console while you click around. The fragment changes, a `hashchange` event fires, and the Network tab shows no new document request. In server logs, every view collapses into the same path.
A tracker has to listen for `hashchange` and send the full `path#fragment` itself. The fragment is missing from the Referer header and from access logs, so nothing downstream will recover it.
Worked example
An old Angular admin tool at `/portal` uses routes like `#/invoices/4412` and `#/clients/88`. The access log shows 3,900 hits to `/portal` and nothing else, and a tracker that listens only to pushState also reports 3,900 views of one URL.
After a `hashchange` listener goes in, the report splits into invoices (1,400), clients (900), reports (350), and the rest. Outgoing links still carry a Referer of plain `/portal`, with no fragment.
How it differs
Hash routing is how the app changes view; a virtual pageview is the event you send to record that change. The routing style decides which browser event fires, `hashchange` or `popstate`. Hash routes also keep their paths invisible to the server, which history-mode routes do not.
Common errors
Assuming server logs show the fragment. Expecting search engines to treat `#/a` and `#/b` as separate pages. Sending a fragment that holds a token, which OAuth implicit flows return after the `#`. Double-counting when both `hashchange` and `popstate` fire. Treating plain `#section` anchor jumps as page loads.
In practice
Look at the address bar to see whether your router uses hash or history mode. If it is hash mode, add the `hashchange` listener to the tracker and strip any token-like fragment values before sending. If you can, move to history mode with a server fallback to `index.html`.