March 4, 2026
How bounce rate is actually computed (and why GA shows 0%)
Bounce rate used to be one of the simplest numbers in analytics. A user landed on a page, did nothing else, and left. One pageview, no second act. That session "bounced." Divide the bounced sessions by total sessions and you have your bounce rate. Clean, intuitive, and — crucially — the same definition everyone agreed on.
Then GA4 happened, and now half the internet is staring at dashboards that report a 0% bounce rate and quietly wondering if their site has achieved perfection. It has not. The metric just got redefined out from under you. Let's walk through what bounce rate actually is, what GA4 turned it into, and how to compute the honest version yourself.
The classic, correct definition
Bounce rate is the percentage of sessions that contained exactly one pageview.
That's it. A "bounce" is a single-page session: the visitor arrived, viewed one page, and left without navigating anywhere else.
bounce rate = single-pageview sessions / total sessions
This is what Universal Analytics reported for over a decade, and it's what TrackWhy reports today. It's a session-level metric, not a page-level one — although you can and should slice it by landing page, which we'll get to.
The appeal is that it's unambiguous. There's no timer, no engagement heuristic, no configuration that changes the number. A session either has one pageview or it has more than one. You can compute it from raw events with a single query and get the same answer every time.
A short history: how Universal Analytics defined it
Universal Analytics (the GA most people grew up with) defined a bounce as a single-interaction session. In practice that meant one pageview with no subsequent interaction hits — no second pageview, no tracked event, nothing.
This had a well-known side effect: if you fired an event on page load (say, a video autoplay or a scroll-depth tracker), UA would count that as a second interaction and the session would no longer bounce. Teams sometimes gamed this accidentally and watched their bounce rates plummet, which was an early preview of the confusion to come. But the core definition was stable: one interaction, one bounce.
What GA4 actually changed
GA4 threw out the session-and-pageview model and rebuilt everything on events. In GA4, a pageview is just an event called page_view, sitting alongside every other event. There's no special "interaction hit" concept anymore.
With the old model gone, the old bounce rate had no foundation to stand on. So GA4 introduced engagement rate as the headline metric and redefined bounce rate as its inverse:
bounce rate (GA4) = 1 - engagement rate
An engaged session in GA4 is a session that meets at least one of these conditions:
- lasted 10 seconds or longer, OR
- had at least one conversion event, OR
- had 2 or more pageviews (or screenviews).
So a GA4 "bounce" is a session that lasted under 10 seconds, fired no conversion, and had only one pageview. That is a much narrower thing than "one pageview." It's a "one pageview AND barely there AND did nothing" session.
These two metrics are not measuring the same phenomenon. The UA-style bounce answers "did they look at one page?" The GA4 bounce answers "were they completely unengaged by every available signal?" Comparing them across a migration is comparing apples to a different fruit entirely.
Why GA4 shows 0% (or numbers that make no sense)
Here's the part that confuses everyone. Several things conspire to push GA4 bounce rate toward zero:
The 10-second engagement timer
GA4's default install includes "enhanced measurement," which fires a user_engagement event and tracks engagement_time_msec — the amount of time the page was actually in the foreground and focused. The moment a session crosses 10 seconds of engagement time, it's flagged as engaged. Forever. It cannot un-engage.
Ten seconds is a low bar. Most people who land on a page and read a sentence clear it. So the overwhelming majority of sessions get marked engaged, engagement rate climbs toward 100%, and 1 - engagement rate collapses toward 0%. A 0% bounce rate doesn't mean nobody bounced — it means almost everyone stayed long enough to trip a 10-second timer.
engagement_time_msec quirks
engagement_time_msec only counts time when the tab is visible and focused. That sounds precise, but it interacts badly with misconfigured tags. If your GA4 tag double-fires, or a tag manager sends an extra event on load, or you've got a custom timer pumping engagement events, sessions get marked engaged immediately and bounce rate flatlines at 0%. A genuinely-zero bounce rate is almost always a measurement artifact, not a fact about your users.
It's hidden by default
GA4 doesn't even show bounce rate in standard reports out of the box. You have to add it as a metric in the report customization UI or in Explorations. Plenty of "GA shows 0%" reports are actually "I added a metric I don't fully understand and it returned a number I didn't expect."
Computing it correctly yourself
If you have an events table — a row per event with a session_id, an event name, and a timestamp — the honest bounce rate is a two-line query. Count pageviews per session, then count how many sessions had exactly one.
SELECT
countIf(pageviews = 1) / count() AS bounce_rate
FROM (
SELECT
session_id,
count() AS pageviews
FROM events
WHERE name = 'pageview'
GROUP BY session_id
)
The inner query collapses raw pageview events into one row per session with a pageview count. The outer query divides single-pageview sessions by all sessions. No timer, no engagement heuristic, no configuration drift. Run it twice and you get the same number twice.
Want it per landing page? Carry the first page of each session through and group by it:
SELECT
landing_page,
countIf(pageviews = 1) / count() AS bounce_rate,
count() AS sessions
FROM (
SELECT
session_id,
count() AS pageviews,
argMin(path, timestamp) AS landing_page
FROM events
WHERE name = 'pageview'
GROUP BY session_id
)
GROUP BY landing_page
ORDER BY sessions DESC
How to actually read the number
Here's the uncomfortable truth: bounce rate is a blunt instrument, and a high one isn't automatically bad.
The mechanical reason is informative. You cannot measure time-on-page for a bounced page. Time-on-page is computed by diffing the timestamps of consecutive pageviews — page A loaded at 0:00, page B loaded at 0:43, so they spent 43 seconds on A. A bounced session has no second pageview, so there's no second timestamp to subtract. The last page of any session has the same problem. This is why naive "average time on page" numbers are so unreliable, and it's the gap that engagement-time metrics were invented to fill.
So a bounce tells you "single page, then gone" — and that can mean two very different things:
- A user hit your "what time does the store close" page, got the answer in four seconds, and left satisfied. High bounce, mission accomplished.
- A user hit your pricing page, found it confusing, and left frustrated. High bounce, problem.
Bounce rate alone can't distinguish these. That's exactly the gap engagement metrics try to fill — they layer time and conversions on top to guess at intent. The honest move is to use both: bounce rate to answer "did they go anywhere?" and engagement signals to answer "did they get anything?"
Segment it, or it's noise
A single site-wide bounce rate is nearly useless. The signal lives in the segments:
- By landing page. A 75% bounce on a blog post that answers a question is healthy. A 75% bounce on a pricing or checkout page is a leak.
- By channel. Organic search traffic bounces differently than a paid campaign you're paying for click by click. Direct traffic bounces differently again.
- By device. Mobile bounce rates run higher almost everywhere; comparing mobile against desktop in one blended number hides both.
Read in context, bounce rate becomes a triage tool: it points at where to look, not what's wrong. Pair it with the page's job. Reference content is supposed to answer and release. Conversion pages are supposed to pull people forward.
How TrackWhy does it
TrackWhy reports the classic definition: the percentage of sessions with a single pageview. We compute it directly from your pageview events — count sessions, count single-pageview sessions, divide — with no engagement timer in the path and no configuration that can quietly move the number.
Because it's computed over the raw event stream, it's drillable. Every bounce rate in TrackWhy can be sliced by landing page, channel, device, country, or any dimension we capture, so you can go from "site bounce is 58%" to "the bounce is all on this one paid landing page on mobile" in a couple of clicks. And because we're cookieless and don't depend on a tag-managed engagement event firing correctly, the number reflects what people did, not whether your measurement plumbing happened to trip a 10-second flag.
In closing
Bounce rate was never a deep metric, but it was an honest one: one page, then gone. GA4 didn't improve it — it replaced it with the inverse of a 10-second timer and then hid the result two menus deep. If your dashboard proudly reports a 0% bounce rate, that's not a perfect site. That's a metric telling you it has stopped measuring anything you'd recognize.
Count the single-pageview sessions, divide by the total, and slice by the dimension that matters. The number's been simple all along. It just needed someone to stop redefining it.