[invisit](https://invisit.dev) · [Docs](https://invisit.dev/docs) · [llms.txt](https://invisit.dev/llms.txt) · [llms-full.txt](https://invisit.dev/llms-full.txt)

# Bot traffic in Google Analytics and others: how to spot and filter it

> How GA4, Plausible, Umami, Fathom, Matomo, PostHog, Cloudflare and Vercel filter bots, what slips past them, and how to clean up your numbers.

- Author: Miraç Berk
- Published: 2026-11-03
- Page: https://invisit.dev/blog/bot-traffic
- Tags: bots, google-analytics, analytics

![Small white robots slipping through a frosted-glass filter toward a white analytics chart, with violet dithered light and soft shadows.](https://invisit.dev/_astro/cover.D1nl_1wr.png)

Bots get into your visitor numbers when they load a page in a real browser and run your analytics script: headless Chrome scrapers, automated tests, page-speed tools and traffic from cloud servers. The major analytics tools filter out bots that name themselves in the user agent; they differ on everything after that, and Google Analytics 4 lets you neither switch its bot filter off nor see what it removed.

Below: how nine tools filter bots, taken from each vendor's docs and tracking script on 3 November 2026, what happened when we pointed eight headless and automated browsers at a test page, and how to find and remove the bots that get through. Crawlers that fetch your HTML without running JavaScript never reach a script-based tool; [AI crawler user agents](/blog/ai-crawlers) covers those, with robots.txt and IP verification.

## Which bots end up in your analytics?

Only bots that execute your tracking script, or that send hits straight to your analytics endpoint, reach your visitor numbers. A plain HTTP fetch of a page, which is what most crawlers do, never runs the script.

| Kind                            | Examples                                                                | How it reaches your numbers                                     |
| ------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------- |
| Headless and automated browsers | Scrapers and test suites built on Puppeteer, Playwright or Selenium     | Loads the page in Chrome or Firefox and runs every script on it |
| Speed and monitoring tools      | Lighthouse, which produces PageSpeed Insights' lab data; browser checks | Loads the page in a real browser, on a schedule or on demand    |
| Cloud-hosted traffic            | Any of the above, run from AWS, Google Cloud or Tencent Cloud           | Arrives from a data-centre address that geolocates to one city  |
| Referrer spam                   | `semalt.com`, `buttons-for-website.com`                                 | Sends a page view with a fake referrer so the domain shows up   |
| Ghost hits                      | Events posted to the collection endpoint with your public site ID       | Never loads your page, so it is missing from your server logs   |

PageSpeed Insights generates its lab data with Lighthouse ([Google](https://developers.google.com/speed/docs/insights/v5/about)), so every test you run there can load your page and your script.

## What does a headless browser look like to analytics?

Headless Chrome announces itself in its default user agent, and one line of configuration hides that. On 3 November 2026 we loaded a test page with eight headless or automated setups on Linux and recorded what a tracker receives: the headers on a `fetch()` sent from the page, as trackers send events, and `navigator.webdriver` as the page's JavaScript reads it. Each capture then went through isbot 5.2.2, the open-source user-agent list Umami's server uses, and through the collector checks of invisit, the tool we build.

| Setup                                           | User agent sent                | `Sec-CH-UA` brands       | `navigator.webdriver` | isbot | invisit  |
| ----------------------------------------------- | ------------------------------ | ------------------------ | --------------------- | ----- | -------- |
| Playwright 1.64, defaults                       | HeadlessChrome 156, Linux      | Chromium, HeadlessChrome | true                  | Bot   | Not sent |
| Playwright, new headless mode                   | HeadlessChrome 156, Linux      | Chromium                 | true                  | Bot   | Not sent |
| Playwright, user agent set to Chrome on Windows | Chrome 155, Windows            | Chromium, HeadlessChrome | true                  | Human | Not sent |
| Puppeteer 25 with Chrome 155, headless          | HeadlessChrome 155, Linux      | Google Chrome, Chromium  | true                  | Bot   | Not sent |
| Puppeteer, user agent set to Chrome on Windows  | Chrome 155, Windows            | Not sent                 | true                  | Human | Not sent |
| Chrome 155 with a window, driven by Puppeteer   | Chrome 155, Linux              | Google Chrome, Chromium  | true                  | Human | Not sent |
| Lighthouse 13.6, default mobile run             | Chrome 155, Moto G Power phone | Chromium, Google Chrome  | true                  | Human | Not sent |
| Firefox 157 with `--headless`, no automation    | Firefox 157, Linux             | Not sent                 | false                 | Human | Counted  |

What the eight runs show:

- **User agent lists catch defaults only.** Three of eight runs said HeadlessChrome. Changing one option made Playwright and Puppeteer look like Chrome on Windows, and Lighthouse, which has not carried its `Chrome-Lighthouse` marker since version 10.0.0, looks like an Android phone.
- **`navigator.webdriver` was true in all seven automated runs**, disguised ones included. Only code inside the tracking script can read it. Of the scripts we downloaded the same day, Plausible's, PostHog's and Vercel's check it; GA4's gtag.js, Umami's, Fathom's, Matomo's and Cloudflare's do not. invisit's script checks it too: "Not sent" in the table means it stopped before sending anything.
- **Headless Firefox without automation looks human from inside the browser.** Its only tell is the network it runs from, which is what data-centre IP filtering looks at.
- **Defaults leave a screen size behind.** Puppeteer reported an 800x600 screen and Playwright 1280x720, both on Linux.

To see what your own tool counts, install Playwright (`npm install playwright`, then `npx playwright install chromium`) and run this script against a page of your site, once plain and once with `--disguise`:

```js title="bot-visit.mjs"
import { chromium } from 'playwright'

const url = process.argv[2]
const disguise = process.argv[3] === '--disguise'

const browser = await chromium.launch()
const context = await browser.newContext(
  disguise
    ? {
        userAgent:
          'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36',
      }
    : {},
)
const page = await context.newPage()
await page.goto(url)
await page.waitForTimeout(5000)
await browser.close()
console.log(`visited ${url}${disguise ? ' with a desktop Chrome user agent' : ''}`)
```

```sh title="Terminal"
node bot-visit.mjs https://example.com/bot-test
node bot-visit.mjs https://example.com/bot-test --disguise
```

Then look for `/bot-test` in your realtime report. We tested the script with Node 24 and Playwright 1.64 on 3 November 2026.

## How does each analytics tool filter bots?

Eight of the nine tools document a filter for known bot user agents, and Cloudflare offers a bot filter without saying how it works. They differ on whether the script checks for automation, whether data-centre traffic is filtered, and whether you can see or change what was removed. As of 3 November 2026, from each vendor's docs, and for the script column from its current tracking script:

| Tool                     | Known bot user agents                         | Script skips automated browsers | Data-centre IPs                        | Referrer spam            | Shows what it removed                     | Rules you can add                                                 |
| ------------------------ | --------------------------------------------- | ------------------------------- | -------------------------------------- | ------------------------ | ----------------------------------------- | ----------------------------------------------------------------- |
| Google Analytics 4       | Google research plus the IAB/ABC bots list    | No                              | Not documented                         | Not documented           | No                                        | IP rules for internal traffic, hostname include or exclude        |
| Plausible                | Yes                                           | Yes                             | About 40,800 ranges, Cloud only        | Yes                      | Per request, in a response header         | Shields: IP, country, hostname, page                              |
| Umami                    | Yes, with isbot                               | No                              | Not in the ingest code                 | Not in the ingest code   | No                                        | IPs and CIDR ranges                                               |
| Fathom                   | Yes, plus requests with no user agent         | No                              | Yes, VPN exits excepted                | Not documented           | Yes: count by type, top countries         | Firewall: pages, IPs, referrers, domains, countries, events       |
| Matomo                   | Yes                                           | No                              | Optional, by organisation and IP range | Yes, list updated weekly | Not documented                            | User agents, IPs, countries, headless browsers, actions per visit |
| PostHog                  | Yes, in posthog-js and at query time          | Yes                             | No; crawler ranges of 6 operators only | Not documented           | Stored bot events, in a Bots tab (alpha)  | Custom bot rules on 15 properties, IP ranges included             |
| Cloudflare Web Analytics | An Exclude Bots filter; method not documented | No                              | Not documented                         | Not documented           | Toggle the filter and compare             | Not documented                                                    |
| Vercel Web Analytics     | Yes                                           | Yes                             | Not documented                         | Not documented           | No                                        | Not documented                                                    |
| invisit                  | 55 patterns, plus requests with no user agent | No                              | No                                     | 29 known spam hosts      | Yes: 14 days, by reason and bot signature | Allowed domains only                                              |

The sections below give each tool's details and sources.

## Does Google Analytics 4 filter bot traffic?

Yes, automatically, and you cannot change it. GA4 excludes traffic from known bots and spiders, identified with Google's research and the IAB/ABC International Spiders and Bots List, and Google says you "cannot disable known bot traffic exclusion or see how much known bot traffic was excluded" ([Google](https://support.google.com/analytics/answer/9888366)). The IAB list itself is paid, from $5,000 to $15,000 a year ([IAB](https://www.iab.com/guidelines/iab-abc-international-spiders-bots-list/)), so you cannot check which user agents are on it.

What you can change in GA4:

- **Data filters** for internal traffic (IP rules), developer traffic and web hostnames. Filters apply only from the moment you create them, and excluded data "will never be available in Google Analytics or BigQuery" ([data filters](https://support.google.com/analytics/answer/10108813)).
- **Hostname filters**, added on 11 June 2026, gained an Include mode on 21 September 2026: list your domains and GA4 drops events from anywhere else, including events with no hostname. Measurement Protocol events are exempt ([What's new](https://support.google.com/analytics/answer/9164320)). A hostname filter does nothing against bots that load your real pages.
- **Unwanted referrals** only stop a domain being credited as a source, up to 50 per data stream; the sessions stay ([unwanted referrals](https://support.google.com/analytics/answer/10327750)).
- **Segments and comparisons** in Explore hide bot sessions from an analysis without deleting them. Standard reports stay as they are.

### The Lanzhou and Singapore bot wave

From mid-September 2025, GA4 properties started recording Direct sessions from Lanzhou, China and from Singapore with almost no engagement ([Google Analytics Community](https://support.google.com/analytics/thread/378622882)). On 10 November 2025 a Google Product Expert, a community volunteer rather than a Google employee, relayed in that thread that Google's teams had identified "a new, targeted form of inauthentic, non-human traffic (bots) that is currently bypassing the standard filtering systems". Its marks were sessions with only `session_start` and `page_view` and unusual concentrations of older device and OS profiles, and "a long-term spam detection fix is in development". The suggested workaround was an Explore segment excluding, for example, sessions from Singapore shorter than 10 seconds.

WIRED reported in February 2026 that US government websites, as measured by analytics.usa.gov, got 14.7% of their visits over 90 days from Lanzhou and 6.6% from Singapore, and that one site owner traced the traffic to Tencent's network, AS132203 ([WIRED](https://www.wired.com/story/made-in-china-niche-websites-are-seeing-a-surge-of-mysterious-traffic-from-china/)). As of 3 November 2026, Google's What's new page lists no change to bot filtering.

## How do Plausible, Umami, Fathom, Matomo and PostHog filter bots?

Each of these five filters known bot user agents; they differ in what they add on top.

### Does Plausible count bots?

Plausible filters bots by user agent, referrer spam, about 40,800 data-centre IP ranges and its own traffic-pattern detection, and says it blocks about 4 billion bot visits a month ([bot filtering](https://plausible.io/docs/bot-traffic-filtering)). Its script also stops when `navigator.webdriver` is set or PhantomJS, Nightmare or Cypress is present. The data-centre filtering "has never been part of the self-hosted release" ([Plausible on GitHub](https://github.com/plausible/analytics/discussions/6210)); Community Edition filters by user agent and referrer spam ([self-hosted comparison](https://plausible.io/self-hosted-web-analytics)).

The trade-off, in Plausible's words: some VPN and iCloud Private Relay addresses fall in data-centre ranges, and those visits are dropped too. A dropped event still gets a 202 answer, with `x-plausible-dropped: 1` in the response headers ([troubleshooting](https://plausible.io/docs/troubleshoot-integration)).

### Does Umami filter bots?

Umami drops every request whose user agent isbot flags, answering it with `{"beep":"boop"}`, unless `DISABLE_BOT_CHECK` is set ([Umami 3.4.1 source](https://github.com/umami-software/umami/blob/master/src/app/api/send/route.ts)). Its ingest code has no data-centre or referrer-spam check and its script no automation check, so Umami would have counted the five runs isbot passed in our test. Self-hosted Umami takes IPs and CIDR ranges in `IGNORE_IP` ([environment variables](https://umami.is/docs/environment-variables)); Umami Cloud has IP filters from the Pro plan ([IP filters](https://umami.is/docs/cloud/ip-filters)).

### How does Fathom block bots?

Fathom classifies each request at ingestion with a bot user-agent list, data-centre and hosting IP ranges (VPN exit nodes excepted) and a check for missing user agents ([bot detection](https://usefathom.com/docs/features/bot-detection)). Fathom shows the result on the dashboard itself: a bot icon counts blocked requests for the date range, split by data-centre IP, bot user agent and empty user agent, with top countries. Bot blocking can be switched off per site, which Fathom suggests when a reverse proxy makes real visitors look like bots.

### How does Matomo exclude bots?

Matomo excludes known bots by default and records them only if you add `bots=1` ([FAQ](https://matomo.org/faq/new-to-piwik/faq_63/)), and it filters referrer spam with a community list it updates weekly ([referrer-spam-list](https://github.com/matomo-org/referrer-spam-list)). Tracking Spam Prevention, in General settings, can block cloud providers (by organisation, and by IP ranges Matomo refreshes every 24 hours), headless browsers, server-side HTTP libraries and chosen countries. It can also cap actions per visit; the default cap is 0, which means off ([guide](https://matomo.org/faq/how-to/block-spam-and-bot-traffic-with-tracking-spam-prevention/)). Matomo notes that a headless browser with a customised user agent gets past it. On Matomo Cloud, where every hit counts toward the quota, Matomo's own advice for using less of it is to filter bots and scrapers before they are tracked ([quota FAQ](https://matomo.org/faq/cloud/how-to-reduce-your-matomo-cloud-quota-usage-with-pre-ingestion-traffic-filtering/)).

### How does PostHog handle bot traffic?

posthog-js drops every capture from a known bot, and from any browser where `navigator.webdriver` is true or HeadlessChrome appears in the user agent or brand list ([troubleshooting](https://posthog.com/docs/product-analytics/troubleshooting)). PostHog also classifies stored events at query time by user agent and by the crawler IP ranges Google, OpenAI, Microsoft, Apple, Perplexity and Ahrefs publish, and says "bots from unpublished IP ranges with spoofed user agents can still evade detection" ([bot detection](https://posthog.com/docs/web-analytics/bot-detection)). Custom bot rules match on 15 properties, IP ranges and screen size among them, and also apply to events already captured.

### Cloudflare Web Analytics and Vercel Web Analytics

Cloudflare Web Analytics has an Exclude Bots filter that makes the data "a closer representation of real user traffic", without documenting how a bot is identified ([dimensions](https://developers.cloudflare.com/web-analytics/data-metrics/dimensions/)). Vercel Web Analytics does not count "traffic that comes from automated processes or accounts", judged by the user agent ([Vercel](https://vercel.com/docs/analytics#bots)), and its script returns before sending anything when `navigator.webdriver` is true or the user agent contains "Headless".

## Why does traffic spike from one city with 100% bounce?

A spike from a single city with 100% bounce and zero engagement time is almost always automated traffic from a cloud data centre. Geolocation databases place a cloud region's addresses in the town where the data centre sits, so every bot in that region lands in the same city, opens one page and leaves.

We measured how strongly. On 3 November 2026 we drew 2,000 random IPv4 addresses per region from each provider's published ranges ([AWS](https://ip-ranges.amazonaws.com/ip-ranges.json) EC2 entries, [Google Cloud](https://www.gstatic.com/ipranges/cloud.json), [Oracle](https://docs.oracle.com/en-us/iaas/tools/public_ip_ranges.json)) and looked each up in DB-IP's free City Lite database, built on 1 November 2026:

| Cloud region                 | Top city in DB-IP    | Share of sampled addresses |
| ---------------------------- | -------------------- | -------------------------- |
| AWS us-east-1                | Ashburn, Virginia    | 97.3%                      |
| AWS us-east-2                | Dublin, Ohio         | 58.0% (Columbus 40.6%)     |
| AWS us-west-2                | Boardman, Oregon     | 61.4% (Portland 23.8%)     |
| AWS eu-central-1             | Frankfurt am Main    | 97.5%                      |
| AWS ap-southeast-1           | Singapore            | 98.3%                      |
| Google Cloud us-central1     | Council Bluffs, Iowa | 100%                       |
| Google Cloud us-east1        | North Charleston, SC | 100%                       |
| Google Cloud europe-west1    | Brussels             | 100%                       |
| Google Cloud asia-southeast1 | Singapore            | 99.8%                      |
| Oracle us-ashburn-1          | Ashburn, Virginia    | 100%                       |

If your spike sits in one of these cities, start from the assumption that it is a data centre. The city is still only a database's guess: WIRED's source traced the Lanzhou wave to AS132203, yet of 4,000 random addresses from that network's announced prefixes ([RIPEstat](https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS132203)), DB-IP put 24.9% in Hong Kong, 17.6% in Singapore, 7.2% in mainland China and none in Lanzhou.

## How do you spot bot traffic in your own reports?

Isolate the spike, then check whether it behaves like people. Each step names the GA4 report; other tools have the same breakdowns under similar names.

1. **Find the channel.** In Traffic acquisition, compare the spike with the same weekday a week earlier. Bot traffic usually lands in Direct, because bots rarely send a referrer.
2. **Break it down by place.** In Demographic details, switch to City and compare it with the data-centre cities above.
3. **Read engagement for the spike alone.** Zero engagement time, one page per session and no key events, across hundreds of sessions, is not human.
4. **Check the technology.** In Tech details, look for one browser version, an old Windows or Linux desktop, and screen sizes that match automation defaults, such as 800x600 for Puppeteer or 1280x720 for Playwright.
5. **Look at timing.** Visits at fixed intervals point to a monitor; a burst of visits to old pages points to a scraper.
6. **Compare with what you own.** No matching rise in sign-ups or sales means the visits are not customers. No matching requests in your server or CDN logs means the hits were posted to the analytics endpoint directly.
7. **Open single visits.** A visit timeline, such as Matomo's Visits Log or invisit's visit log, shows what one suspected bot did step by step.

## How do you remove bot traffic that got through?

Block it for the future in the tool's settings, and clean past reports where the tool allows it; most tools can only do the first.

| Tool                     | Stop future bot hits                                      | Clean up past reports                                  |
| ------------------------ | --------------------------------------------------------- | ------------------------------------------------------ |
| Google Analytics 4       | Data filter with an IP rule, or a hostname include filter | Explore segments only; filters are not retroactive     |
| Plausible                | Shields by IP, country, hostname or page                  | Dashboard filters; Shields are not retroactive         |
| Umami                    | IP filters, or `IGNORE_IP` when self-hosted               | Report filters and saved segments                      |
| Fathom                   | Firewall block or allow lists                             | Data removals, which can be undone                     |
| Matomo                   | Tracking Spam Prevention, excluded user agents and IPs    | Segments; exclusions are not retroactive               |
| PostHog                  | Custom bot rules                                          | Filter on Is bot, which rules apply to past events too |
| Cloudflare Web Analytics | Not documented in Web Analytics                           | Exclude Bots filter                                    |
| Vercel Web Analytics     | Not documented                                            | Not documented                                         |
| invisit                  | Allowed domains                                           | Not possible                                           |

Sources: [Plausible](https://plausible.io/docs/bot-traffic-filtering), [Fathom firewall](https://usefathom.com/docs/script/firewall) and [data removals](https://usefathom.com/docs/features/data-removals), [Matomo exclusions](https://matomo.org/faq/how-to/faq_126/), [PostHog](https://posthog.com/docs/web-analytics/managing-bot-traffic). Excluding your own visits is a separate job, covered in [how to exclude internal traffic](/blog/exclude-internal-traffic).

The surest block happens before analytics: at your CDN or server, by network. The weather-site manager in [WIRED's story](https://www.wired.com/story/made-in-china-niche-websites-are-seeing-a-surge-of-mysterious-traffic-from-china/) blocked four Tencent, Alibaba and Huawei networks and saw daily bot visits fall from 127,000 at their peak to just above 2,000.

## How does invisit keep bots out of visitor counts?

invisit, the tool we build, stops its script in any browser where `navigator.webdriver` is true, so Playwright, Puppeteer, Selenium and Lighthouse runs send nothing; the `debug` option lifts that for your own tests. The collector then checks every tracker request before anything is stored, in this order:

1. **Origin.** When you list allowed domains, events sent from any other site are refused.
2. **Rate.** Per-minute limits per address and per visitor.
3. **User agent.** 55 patterns: search engines, AI crawlers, social previews, uptime and page-speed tools, 7 headless and automation markers (HeadlessChrome, PhantomJS, Puppeteer, Playwright, Selenium and WebDriver, Cypress, Lighthouse), HTTP libraries such as curl, Python requests, Go and axios, and the words bot, crawler and spider. Cubot phones and Abbott are exempt. A request with no user agent counts as a bot.
4. **Browser headers.** A `Sec-CH-UA` brand list naming a headless browser, `Sec-Fetch` headers showing a form, frame, image or page navigation instead of a script's fetch, and `Sec-Purpose` marking a prefetch or prerender.
5. **Referrer spam.** A page view referred by one of 29 known spam hosts, or a subdomain of one, drops its whole batch.

A request refused as a bot or as spam gets a normal 202 answer with nothing accepted, so the bot sees no error and does not retry. Nothing is written and nothing is billed. invisit counts each refusal per hour by reason and bot signature, keeps the counts for 14 days, and shows them in site settings under Blocked traffic, through the [REST API](https://invisit.dev/docs/api/rest.md) at `GET /v1/sites/{site_id}/ingest/rejections`, and through the [MCP server](https://invisit.dev/docs/api/mcp.md). The same checks run behind the first-party proxy in `@invisitdev/server`, which forwards the user agent, the `Sec-Fetch` and `Sec-CH-UA` headers and the visitor's address. Short-link clicks from bots, crawlers, uptime probes and prefetches still redirect but are not recorded.

Crawlers are a separate report. With [`@invisitdev/server`](https://invisit.dev/docs/server/sdk.md#crawler-reads) on your server, requests that look automated go to the collector, which keeps those matching one of 86 crawler families in 8 groups, from AI training to archivers, and drops the rest. Crawler reads never touch visitor numbers and are never billed.

What invisit does not do, and the tools above do better: it has no data-centre IP list and judges no behaviour beyond per-minute rate limits. Of the eight test visits it counted one, headless Firefox run without automation, which only a network check catches; Plausible, Fathom and Matomo filter the cloud addresses such a bot runs from, and invisit lets them through. Once a bot is counted, invisit cannot hide it: filters narrow a report to a city, browser, OS, device type, entry page or referrer, enough to find a spike, but there is no exclude filter, IP block or data removal.

## Checklist before you trust a traffic spike

- [ ] Compare the spike with sign-ups, sales or leads in the same hours.
- [ ] Break it down by city and check it against the data-centre cities above.
- [ ] Read engagement time, pages per visit and screen size for the spike on its own.
- [ ] Look for the same visits in your server or CDN logs.
- [ ] Run `bot-visit.mjs` against your site, plain and disguised, to learn what your tool lets through.
- [ ] Test any permanent filter before you activate it: in GA4 an active data filter drops matching data for good.

The trap to avoid is blocking a whole country. Real visitors share countries, and sometimes data-centre addresses, with the bots: Plausible drops some VPN and iCloud Private Relay visits for exactly that reason. Filter on the combination the bots share, such as one city, zero engagement and one screen size, the way the GA4 workaround paired Singapore with sessions under 10 seconds.
