GA4 Direct Traffic Is Lying to You. I Spent a Weekend Proving It.
Someone posted on r/analytics last month asking why 60% of their traffic showed up as Direct/(none) in GA4. The replies were a mix of “that’s normal” and “you have bots.” Both were right, which is the whole problem with this metric. I went through a similar investigation on our own site two weeks ago and came out the other side convinced that Direct/(none) is where GA4 throws everything it cannot explain, and most of it has nothing to do with someone typing your URL into their browser.
GA4’s junk drawer
Direct/(none) is supposed to mean a user typed your URL directly or used a bookmark. But GA4 also dumps traffic into this bucket when the referrer header is missing, when HTTPS-to-HTTP transitions strip attribution, when mobile apps open links in embedded browsers that do not pass referral data, when UTM parameters are absent from campaign links, and when bots hit your site without any headers at all. So “direct” really means “we have no idea where this came from, good luck.”
Last Tuesday, 400 sessions appeared out of nowhere
I opened GA4 on a Tuesday morning and the real-time report showed a traffic spike that made no sense for our size. About 400 sessions in 18 hours, all Direct/(none), all hitting the homepage, all showing 0 seconds average engagement time. Nobody was reading anything. And nobody clicked a second page.
My first instinct was that maybe we’d gotten linked somewhere and the referrer was being stripped. But 400 sessions with zero engagement pointed somewhere uglier.
So I started in the source/medium report, filtered to Direct/(none) only, and broke it down by landing page. Every single session hit /. No deep pages, no blog posts, nothing. Real humans who bookmark your site or type the URL occasionally land on interior pages because they’re returning to something specific they read before, which means a report showing 100% homepage traffic is almost certainly bots.
Building an Explore report to check time distribution made it obvious. The sessions clustered into two bursts: one around 2am UTC, another around 6am UTC. And humans do not visit a small SaaS site in synchronized waves at 2am. Bots absolutely do.
I checked device and browser breakdowns last. The sessions showed up as desktop Chrome on Windows, which is exactly what sophisticated bots spoof because it is the most common user agent string on the internet. Nothing in the GA4 data alone would have flagged these as fake if I had not looked at the timing pattern first.
Cloudflare fixed it in about ten minutes
Our site sits behind Cloudflare, so I pulled up the WAF analytics for the same time window. The traffic was coming from a Tencent-owned ASN, which I recognized from a thread on Hacker News where someone documented the same pattern hitting their marketing site. So I created a WAF rule that issues a managed challenge to any request from that ASN. Bot traffic dropped to zero within a day, and our Direct/(none) percentage went from 52% back down to around 27%.
What “normal” even looks like
About 25-30% Direct/(none) for a small SaaS site. Some actually direct, some dark social, some email clients stripping headers. You cannot get it to zero. But above 40%, something is wrong.
UTMs are annoying but they’re the only damn defense
Every link you control should have UTM parameters. Newsletter links, social posts, links in your own product that point back to your marketing site. Because without them, GA4 classifies that traffic as direct, and you lose the ability to tell whether your latest email campaign drove 50 visits or zero. The non-bot portion of Direct/(none) is almost entirely this problem.
Server-side analytics tools like Plausible or Fathom help cross-reference because they capture referrer data differently than GA4’s client-side JavaScript, which gets blocked by ad blockers and privacy extensions at rates that would alarm you if you checked. Running both gives you a second opinion when the GA4 numbers look suspicious.
So what do you actually do with this
If your GA4 Direct/(none) percentage seems high, the debugging process is pretty mechanical. Filter to direct traffic only, break down by landing page, check time distribution in Explore, and look at device and browser patterns for anything suspiciously uniform. GA4’s interface makes this harder than it should be (navigating Explore reports is awful, and tools like dassi can speed up the clicking-through-nested-menus part considerably). But the process itself is straightforward once you know what you are looking for.
And if you find bots, the fix is almost always at the CDN or WAF layer, not in GA4 itself. GA4 has no built-in bot filtering worth mentioning. Cloudflare, Fastly, or whatever sits in front of your origin server is where you solve it. I wrote about a similar debugging exercise for Google Cloud Console recently, and it is the same story: the tool showing you the problem is rarely the tool that fixes it.
Or start with your browser workflow and see what else is hiding in there. Direct/(none) is GA4’s way of shrugging at you, and it will keep shrugging until you stop trusting it.