A referrer is something the browser reports. A UTM is something you write into the link. Direct is what is left when neither shows up. Here is how each one is read, why referrers go missing, and exactly how GhostMetrics sorts them into Traffic Types.
Every source report is built from two inputs: the referrer the browser hands over, and the UTM parameters you put in the link. When both are empty, the visit is "direct", which is a label for missing information rather than a channel.
gemini.google.com is AI while google.com is Search. A host that is not in the table is Other, shown under its registrable domain.Short on time? Jump to the numbered audit at the end. It takes about 20 minutes on a live site.
A referrer is the address of the page the visitor came from, reported by the browser. GhostMetrics' tracker reads it from document.referrer on every pageview and sends it as-is. You do not control it, and the visitor's browser, the linking site and any app in between can all alter or drop it.
A UTM parameter is a query-string label you add to a link yourself: utm_source, utm_medium, utm_campaign (plus utm_term and utm_content, which GhostMetrics does not store). It travels inside the URL, so it survives anything that strips the referrer header. The cost is that it only exists on links you tagged.
Direct is the absence of both. In GhostMetrics a pageview is "Direct" when its stored referrer is an empty string. That covers a typed URL and a bookmark, but also every case where the referrer was stripped. A direct row cannot tell you which. Our direct traffic explainer covers the causes in depth.
Referrer data answers "which sites send me people, when the browser cooperates"; UTM data answers "which of my campaigns worked, when I tagged the link". Neither alone is a complete source report.
Each pageview is classified once, when it arrives. The tracker sends the browser's referrer as it stands; the server runs one function over it, normalizeReferrer, and stores the result as a host and a type next to the raw referrer. The Traffic Type donut counts those stored types. Older pageviews with no stored type are classified with the same function when you view them.
The function checks the referrer in this order, and the first rule that applies wins.
| Order | Result | Rule |
|---|---|---|
| 1 | Direct | The referrer is empty |
| 2 | Other, host left blank | The value cannot be used as a web address (an unrecognised app address, a file: link, text that does not parse). The Referrers table labels it (app or unknown) |
| 3 | Other, host (ip) | The host is an IP address or localhost |
| 4 | Internal navigation | The host is on the same registrable domain as the site that received the pageview |
| 5 | Search | The host is google. plus a country domain from a built-in list, or a search host in the table (Bing, DuckDuckGo, Yahoo, Baidu, Ecosia, Brave Search, Kagi, Startpage, Qwant, Yandex, Naver) |
| 6 | AI or Social | The host, or a parent of it, is in the table under that type |
| 7 | Other | Anything else, stored under its registrable domain |
Details that decide how far to trust the result:
Host, not substring. Before matching, the function lowercases the host and strips one leading www., m., mobile., l., lm., out. or away.. Then it tries the full host against the table, then each shorter suffix, and the longest match wins. That is how subdomains resolve: l.facebook.com becomes facebook.com (Social) and images.bing.com becomes bing.com (Search), so one source is one row.
Google needs special handling. Google search lives on google. plus a country domain, such as google.com, google.co.uk or google.de, and those are Search. Every other Google host is not a search results page. gemini.google.com is in the table as AI. mail.google.com and docs.google.com match nothing, so they are Other and appear under the host google.com. A Google country domain that is not on the built-in list is Other too.
The AI type has a short, fixed list. chatgpt.com, chat.openai.com, perplexity.ai, claude.ai, gemini.google.com, copilot.microsoft.com, you.com and poe.com. Mind the subdomain: copilot.microsoft.com is AI, while microsoft.com on its own is Other. Only visits that arrive with a referrer from those hosts count. An assistant that strips the referrer lands in Direct.
Social covers the big names plus Hacker News. Facebook, Instagram, X and Twitter, t.co, LinkedIn, Reddit, YouTube, Pinterest, TikTok, Threads, Bluesky, mastodon.social, Tumblr and news.ycombinator.com, with their short-link hosts. Reddit has one extra rule: when the path starts with /r/ and a subreddit name, the stored host is reddit.com/r/ plus the lower-cased name, so each subreddit is its own row. No other site keeps any part of the path.
Other is the long tail, and it is not a judgement. It holds partner sites, forums, newsletters, github.com, microsoft.com, and any search engine or network the table does not name. The table is fixed in code, and hosts are never added to it automatically. Five Android apps are recognised from their app addresses: the Google app counts as Search, and the X (Twitter), Facebook, LinkedIn and Reddit apps as Social.
Your own domain is detected by registrable domain. The comparison uses the last two labels of the host, or three for domains such as co.uk, so blog.example.com counts as example.com. On shared hosts such as github.io or pages.dev the unit is three labels, so your site does not hide its neighbours. The match uses the domain saved for the site; if none is saved, nothing is treated as internal.
UTMs are ignored here. A visitor who clicks a newsletter link tagged utm_medium=email from a mail client that sends no referrer counts as Direct in Traffic Type, while the same visit appears under newsletter / email in the UTM Breakdown table. If a web mail page passes its own address, that visit lands in Other. There is no Email, Paid or Campaign type. The only place a UTM label shows up is that separate table.
The donut legend shows each type as a percentage, and a note under it gives the visit count behind the chart, which is lower than Pageviews because internal navigation is excluded. Clicking a legend entry filters the dashboard to that type.
A visit lands in Direct when the referrer is empty at the moment gm.js reads it. Four mechanisms account for most of that, and none of them is detectable from inside the page.
Current major browsers apply a default policy of strict-origin-when-cross-origin when the site sets none (MDN's Referrer-Policy page documents this; check it for current browser support). Between different sites, the destination receives only the origin, for example https://example.com/, never the path or query. A linking site can go further: Referrer-Policy: no-referrer on the page, or rel="noreferrer" on the link, removes the referrer entirely.
Referrer-Policy: no-referrer
<a href="https://yoursite.com/" rel="noreferrer">your link</a>
Under the default policy, a click from an HTTPS page to an HTTP page sends no referrer. If your site is reachable on HTTP, or a redirect passes through an HTTP hop, HTTPS traffic arrives as Direct.
Native mobile apps, desktop clients and embedded browsers open links through the operating system or their own web view. Many do not set a referrer at all. Behavior varies, so test the link.
"Dark social" means links shared where no analytics tool can follow: private messages, group chats, copied-and-pasted URLs, email forwards. A bookmark and a pasted link look the same, so the honest term for that bucket is "unattributed", not "typed in".
When a visitor's browser sends Global Privacy Control or Do Not Track, the tracker returns before sending anything. That visit is absent from every panel, not misfiled as Direct.
The Sources tab has a Referrers table with three columns: Source, Visits and % of referred. Source is the stored host from the classifier above, not the raw string, so www.google.com and google.com are one row. The table lists the top 20 sources by count for the selected period, has a search box and a CSV button, and leaves out both Direct visits and internal navigation. The Overview tab shows a Top Referrers list (top 5) from the same data. The Live tab and the day drill-down (click a day in the traffic chart; top 15) instead show the raw referrer string, which can include your own pages.
Five reading rules follow from how those numbers are produced.
document.referrer does not change on pushState, so one search click can add several Search rows.google.com as Search and google.com from mail.google.com (Other) are separate rows with the same label, and the table does not show the type. The Traffic Type chart shows which bucket each belongs to.referrer next to the stored referrer_host and referrer_type columns.Read Traffic Type for the rough mix, then Referrers for the hosts inside it. The weekly dashboard routine shows where this fits in a 15-minute review.
Tag every link you place on a surface that strips or lacks a referrer: newsletters, social bios, QR codes, PDFs, partner emails, paid placements. Do not tag links between your own pages; the UTM replaces the real source on click.
Use a fixed lower-case vocabulary so one campaign is one row. A sample set:
| Placement | utm_source | utm_medium | utm_campaign |
|---|---|---|---|
| Newsletter issue | newsletter | issue-42 | |
| Link in a social bio | social | profile-link | |
| Partner mention | partner-name | referral | spring-collab |
| QR code on a flyer | flyer | city-meetup |
https://example.com/pricing?utm_source=newsletter&utm_medium=email&utm_campaign=issue-42
What the tracker does with it:
utm_source, utm_medium and utm_campaign from the landing URL. utm_term and utm_content are dropped.sessionStorage, which clears when the tab closes. Later pageviews and custom events in that tab, whose URLs carry no UTMs, get the saved values. This is first-touch within the session. Nothing carries over to a new visit.The UTM parameters guide covers naming and common mistakes; landing page and campaign analytics applies tagging to a landing page test.
GA4 solves the same problem with a bigger machine. Per Google's channel group documentation, the default channel group assigns traffic to channels such as Direct, Organic Search, Paid Search, Organic Social, Email, Referral, Affiliates, SMS and Unassigned by matching source and medium against rules and maintained lists of search, social, video and shopping sites. Direct means the source is "(direct)" and the medium is "(not set)" or "(none)", so it is the same fallback you get here. Google keeps the default group fixed, but you can build custom channel groups with your own rules. Scopes differ too: there are session-scoped and first-user-scoped versions of the default group, which rely on GA4's cookie-based identifiers.
The real difference is how UTMs feed the result. In GA4, utm_medium=email or utm_medium=social moves a session into the Email or Social channel directly. In GhostMetrics' Traffic Type chart, UTMs have no effect; they appear only in their own table.
| Approach | Input it reads | Categories | Where it fits |
|---|---|---|---|
| GhostMetrics Traffic Type | Referrer string only | 5 fixed: Search, Social, AI, Direct, Other | Quick mix check on a cookieless setup; no configuration |
| GhostMetrics UTM Breakdown | utm_source, utm_medium, utm_campaign, first-touch per tab | Whatever labels you wrote | Judging your own campaigns, newsletters and print |
| GhostMetrics export + your rules | Raw rows with referrer, UTM columns and URL | Any grouping you define in a spreadsheet or script | Custom channel logic, your own host groupings, per-referrer page analysis |
| GA4 default channel group | Source, medium and campaign (including UTMs), plus Google Ads data when linked | Rule-based set (Email, Paid Search, Organic Social and others) | Paid media with Google Ads, large multi-channel programs |
| GA4 custom channel group | Same dimensions, your own rules | Yours | Teams that need channel definitions to match their reporting |
GA4 wins where attribution is the job: Google Ads integration, channel rules that react to UTMs, multi-session user paths. A cookieless tool trades those away on purpose. GhostMetrics has no per-user history, no channel editor and no paid-media integration. The alternatives roundup and the cookieless tracking explainer lay out what each side gives up. Check Google's current documentation for the channel list.
If you only need the plain mix of search, social, AI assistants and direct, the five-type chart is enough, as long as you remember that Other means "not in the table" and that Direct includes every stripped referrer.
google.com row inside Other comes from a Google product that is not search, such as mail or docs.referrer_host and referrer_type if you want your own channel rules. Save the grouping so you can repeat it monthly.GhostMetrics shows the referrer table, the Traffic Type donut and the UTM Breakdown on every plan, with no cookies and no consent banner required for the tracker itself. Install one script tag, tag your links, and the Sources tab fills in. The free plan is free forever: one site, unlimited pageviews, real-time dashboard, every view, full history. Raw export as CSV or JSON is available on every plan, so you can apply your own channel rules to the referrer and UTM columns.
Pro is $9.99/mo flat with unlimited pageviews and unlimited websites (fair-use ceiling of 250), plus a public shareable dashboard URL, with a 30-day free trial. A card starts the trial, nothing is charged for 30 days, and you can cancel anytime.
Start free, no card See the live demoDisclosure: GhostMetrics is built by Null Agency and we use it on this site, so we are not a neutral party about our own product. The GA4 comparison is qualitative and based on Google's public channel group documentation; check Google's current documentation before relying on any channel rule. Browser behavior around referrers varies by browser version and by app.