Some of the visitors in your analytics are programs, and some of the referrers are advertisements. Here is how to recognize both, what GhostMetrics drops before it stores anything, which network traffic it leaves out of your numbers, and what no tracker can promise.
Bot traffic is automated requests that get counted as visits: crawlers, scrapers, uptime monitors, audit tools and headless browsers. Referrer spam is junk or fake referrer values that appear in your referrer table so that you will notice, and maybe visit, the domain behind them.
Bots are not one thing. A search crawler, an uptime monitor, a price scraper and a vulnerability scanner differ in whether they run JavaScript, announce themselves or look like a person.
Referrer spam is a separate problem with a different motive. The goal is not to read your pages. It is to put a domain into your referrer report. In crawler spam a real bot requests your pages with a spam domain in the referrer header. In ghost referrals nothing visits your site: the hit is sent straight to the analytics endpoint with a made-up referrer. The tell for crawler spam is a referrer domain with no page linking to you.
| What you see | Common cause | Where to look in GhostMetrics |
|---|---|---|
| Pageviews jump on one page, nothing else moves | A scraper, a monitor, or one real link going around | Click the day's bar in the daily chart; the day panel lists top pages |
| An unfamiliar domain sits in the referrer list | Referrer spam, or a real site you have not noticed | Referrers table and its search box |
| A burst of visits with no referrer | Untagged automation, or the ordinary causes of direct traffic | Referrers and source types, then the direct traffic guide |
| One hosting or cloud network behind many hits | Automated traffic from a data center | The left-out line under the stat cards, then the Organizations panel (Who's Looking tab) |
| Visits from a country you do not serve, round the clock | Proxy rotation, or an audience you did not expect | Top Countries panel and the raw export |
| Hits spaced almost evenly in time | A scheduled job | Raw export, sorted by timestamp |
A server log records every HTTP request the server answers. A script-based tracker records only what a browser engine reports after it has loaded your page and run the tracking script.
Most simple crawlers fetch the HTML, read the links and move on. They do not execute JavaScript, so the tag never runs and your analytics never hears about them. The bots that do reach a script-based tracker run a full browser: headless scrapers, rendering crawlers, uptime monitors and any bot written to look like a person. That group is smaller, but it is the one most likely to be mistaken for real visitors.
So the two numbers answer different questions, and a gap between them is normal. For the mechanics of cookieless collection, see cookieless tracking explained. One more way a visit never reaches the dashboard: the tracker reads the browser's Global Privacy Control and Do Not Track signals and sends nothing when either is on.
Layer one runs at ingest and decides what is stored; layer two runs when numbers are read and decides what is counted (next section). The ingest filter lives in the Worker that receives the tracker's beacons, and every event type goes through it.
A request is dropped if any one of three things is true:
| Class | Strings the filter looks for |
|---|---|
| Automation and headless browsers | HeadlessChrome, Headless, Playwright, Puppeteer, Selenium, PhantomJS |
| Crawlers | bot, crawl, spider, slurp |
| HTTP libraries and command-line tools | curl/, wget/, python, libwww, httpclient, okhttp, java/, go-http-client, node-fetch, undici, axios, got ( |
| Monitoring and audit tools | lighthouse, pingdom, gtmetrix, uptimerobot, datadog, newrelic, checkly, synthetic |
A dropped request gets an HTTP 204, the same status a stored beacon gets, and no row is written. The check runs before the rate limiter and any database write.
Because the event is never stored, the dashboard has no counter for what this layer dropped and no way to review it. A false positive is invisible and cannot be recovered. The match is also a plain substring, so a real browser whose User-Agent happens to contain one of these strings, a device model name containing bot for example, is dropped with the bots.
The check also runs once, when the event arrives. Adding a pattern later does not re-evaluate rows that are already stored. The network layer below works the other way: it is applied at read time, to all history.
After the User-Agent check, ingest applies a rate limit of roughly 100 events a minute per source address. Requests over it get a 429 and are not stored. It bounds abuse; a slow bot stays under it.
www dropped, and given a type, but it is never removed. A spam domain appears as a row like any other.A related point about referrer reporting: source types come from a host table, not from searching the referrer string for a name. A table maps known hosts to a type. The types are Search (Bing, DuckDuckGo, Google's country domains and others), Social (Facebook, Reddit, LinkedIn and others), AI (ChatGPT, Perplexity, Claude and others), Direct (an empty referrer) and Other (any host not in the table). The match is on the host itself, so a spam domain that merely contains a search engine's name is typed Other. A fabricated referrer that names a real search or social host is still typed Search or Social, because the type follows the host as sent.
| Kind of automated traffic | Reaches the dashboard? |
|---|---|
| Crawler that fetches HTML and does not run scripts | No: the tracker never runs, so no beacon is sent |
| Rendering crawler or headless browser that announces itself | No: dropped at ingest by User-Agent |
| Lighthouse run, uptime or synthetic monitor | No: dropped at ingest by User-Agent |
| Script posting events with a default library User-Agent, or none | No: dropped at ingest |
| Script or headless browser on a listed data-centre network, with an ordinary User-Agent | Not in the default numbers: stored and flagged, then left out when numbers are read; Show them adds it back |
| Headless browser with an ordinary User-Agent on a network not on the list | Yes: nothing in either layer tells it apart from a person |
| Bot traffic arriving through residential connections | Yes: a home connection is not on the data-centre list, and the residential label only hides rows in one panel |
| Spam domain in the referrer field | Yes: it shows as a row in Referrers, usually typed Other, unless the hit came from a listed data-centre network |
Each stored hit carries the number and name of the network it came from, taken from the Cloudflare edge at request time. The address itself is not stored. Two mechanisms use the network, and they are easy to confuse: one is a filter and one is a view.
A curated list of data-centre network numbers is checked against each stored hit. It includes Amazon AWS, Google's service and cloud networks, DigitalOcean, OVH, Hetzner, Linode, Vultr, Oracle Cloud, Alibaba Cloud, Tencent Cloud and several other hosting providers. The reasoning is that a visit from one of these is a script, a crawler or a monitor, not a person at a browser.
A hit from a listed network is flagged at ingest, but the row is stored. The exclusion happens when the dashboard reads your numbers: every statistic leaves out rows whose stored network number is on the current list. A change to the list therefore applies to old rows too, and stored events are never rewritten.
A network-only test has a cost. Cloudflare WARP, iCloud Private Relay and Microsoft's networks are left off the list on purpose because they carry real people. And a person browsing through a VPN or proxy that runs on a listed network is left out with the bots.
To see what was left out, look under the stat cards on the Overview tab. A line reads "N pageviews from scripts and crawlers (data-centre networks) were left out" for the period. The owner's dashboard has a Show them button on it: press it and every panel, the Organizations panel included, recomputes with those pageviews included, and a Hide them button replaces it. A published dashboard shows the count but not the button. The raw export is not filtered: it holds every stored row, with the network number, network name and the ingest-time flag as columns.
The Organizations panel, on the Who's Looking tab, groups hits by network name and hides residential ISPs by default, because a home broadband provider is usually a person, not a company visiting you. The button reads Excludes residential ISPs, with a count of hidden networks. Residential is decided by a list of named consumer networks plus generic words such as broadband and residential matched in the network name.
For spotting automation, the useful part is the Business side. Listed data-centre networks are already left out of the panel by default, so a hosting-looking name you do see there is a provider that is not on the list, and its hits count in every number. Bots routed through home connections are classified as residential and sit behind the badge, so when hunting a spike, turn the badge on and press Show them.
On a customer's published dashboard, neither the badge toggle nor Show them is available to viewers. Our own demo keeps the badge toggle, because it exists to show the feature.
For a routine that catches spikes early, the weekly dashboard guide puts the referrer and trend checks in a 15-minute weekly pass.
GA4 approaches this with more machinery, and it is the stronger tool in places. Google's documentation describes automatic exclusion of traffic from known bots and spiders, maintained by Google. The admin interface offers internal-traffic rules and a way to mark domains as unwanted referrals. Check the current Google Analytics documentation for details, because they change. Our GA4 alternative guide covers the wider comparison.
| Question | GA4 | GhostMetrics |
|---|---|---|
| Known-bot exclusion | Automatic, list maintained by Google; check current docs | User-Agent patterns at ingest, plus a data-centre network list at read time |
| Unwanted referrals | Configurable in admin; check current docs | None; the referrer is reduced to its host and typed, never removed |
| Seeing what was filtered | Check current docs | Data-centre pageviews: a count and a Show them button. Ingest drops are never written |
| Raw rows to investigate | BigQuery export; check current docs | CSV or JSON export on every plan |
If you run paid campaigns and need Google to discard junk traffic before it distorts audiences, GA4's managed filtering is a real advantage. If you want a small, cookieless tool where you can read every stored row, a short filter (User-Agent at ingest, data-centre networks at read time) with a full export is easier to reason about.
GhostMetrics drops requests with no User-Agent and requests from known headless browsers, crawlers, HTTP libraries and monitoring tools before they are stored, leaves pageviews from listed data-centre networks out of every default number, then lets you audit the rest: referrers, networks, countries and a raw export of every stored row. It does not claim to stop a bot that looks like a browser on an ordinary network, and it has no referrer blocklist. It sets no cookies, stores no IP addresses (they are hashed) and keeps no cross-session identifier. Install is one script tag in <head>, about 3 KB compressed on the wire.
The free plan is free forever, no card: one website, unlimited pageviews, a real-time dashboard, every view and full history. Raw event export as CSV or JSON is on every plan, including free. Pro is $9.99/mo flat with unlimited websites (fair-use ceiling of 250) and a public shareable dashboard, and it starts with a 30-day free trial that needs a card and charges nothing for 30 days.
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. GA4's bot handling and settings are set by Google and change over time; check the current Google Analytics documentation before relying on any detail.