Why Google Analytics doesn't tell you what happens in your store
Measured today on a real WooCommerce store: 47,956 searches in a month, and not one of them changed the URL, which is the only thing Google Analytics looks at to detect a search. Nor would it know which ones returned zero results: its event doesn't carry that. Here is what GA misses in a store, and how to log it yourself today with a 30-line mu-plugin.
Google Analytics is the default answer to "how do I know what's happening in my store?". You paste the snippet, charts appear, and it feels like everything is measured.
Today I checked what it actually sees on a real WooCommerce store. Specifically, in the part that says the most about a customer: what they type into the search box.
This is data from a collectibles store, from 11 August to 10 September 2026 (31 days), taken from its search log:
| Searches | 47,956 |
| Sessions that searched | 10,817 |
| Sessions that searched and clicked nothing | 5,898 (54.5%) |
| Sessions that searched and ended in an order | 69 |
| Revenue from those orders | €5,713.80 |
(A search is a complete search, not a keystroke: "lu", "luf" and "luffy" typed in a row count as one.)
Now the question: how many of those 47,956 appear in Google Analytics' site search report?
GA only sees a search if the URL changes
I checked the official Google Analytics help today, 11 September 2026. GA4 enhanced measurement records a search (the view_search_results event) when a results page loads, and it recognises one by a parameter in the URL. Five by default: q, s, search, query and keyword.
The native WooCommerce search works that way: you search and land on yourstore.com/?s=luffy&post_type=product. GA sees it.
But almost every modern store search, this store's included, along with most "instant search" plugins, shows results on the same page, as you type, without navigating anywhere. I went through the code of this store's search: it never touches the URL, and picking a product opens it directly.
So the answer is: none. Zero out of 47,956. Not because GA is misconfigured, but because it is built around results pages, and here there is no results page.
And even if it saw them, it would miss what matters
Say your search does use ?s=. GA now sees the search. What does it store?
According to the same documentation, the event carries the search term (search_term). That's it. It doesn't carry how many results came back.
Which means that, without extra setup, GA can't tell you the thing you most need to know about your search: which searches return zero. The product people ask for and you don't stock, or do stock but can't be found because it's named differently. That list is probably the most profitable one in all your analytics, and the standard report doesn't have it.
What gets left out, by name
Go back to the table. 5,898 sessions searched for something and clicked no result. More than half.
These are people who wrote, in their own words, what they wanted to buy. There is no more direct data in a store: it isn't a visit that might be a bot, or a click that might be a mistake. It's someone telling you "I'm looking for this". And they left without touching anything you showed them.
In Google Analytics those sessions are short visits. There's no way to know they searched, what for, or that the results didn't convince them.
The other gap: consent
One more check, done today on the HTML of this same store's home page. It loads Google Tag Manager with a cookie manager, and starts with this:
gtag("consent", "default", {
"analytics_storage": "denied",
"ad_storage": "denied",
...
});
That's correct in Europe and it's what it should do. But it has a consequence: until the visitor accepts cookies, GA can't set analytics cookies, so it can't follow that person from page to page as a single visit. How many people decline the banner varies a lot from store to store, and I won't make up a figure: check yours in your cookie manager's dashboard.
What can be said is that a search log on your own server, with no cookies and no personal data, doesn't depend on that banner.
How to log searches yourself, today
This works if your store uses the native WooCommerce search (or any search that ends on a ?s= page). Save this as wp-content/mu-plugins/search-log.php. You can create the mu-plugins folder if it doesn't exist; anything inside is activated automatically:
<?php
/**
* Plugin Name: Search log
* Description: Stores every store search along with how many results it returned.
*/
add_action('template_redirect', function () {
if (is_admin() || !is_search() || is_paged()) {
return;
}
global $wpdb, $wp_query;
$table = $wpdb->prefix . 'wr_searches';
// Create the table the first time.
if (get_option('wr_searches_v') !== '1') {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta("CREATE TABLE $table (
id bigint unsigned NOT NULL AUTO_INCREMENT,
term varchar(200) NOT NULL,
results int unsigned NOT NULL,
created datetime NOT NULL,
PRIMARY KEY (id),
KEY term (term)
) {$wpdb->get_charset_collate()};");
update_option('wr_searches_v', '1');
}
// Only the term and the result count: no IP, no user.
$wpdb->insert($table, [
'term' => mb_substr(mb_strtolower(trim(get_search_query(false))), 0, 200),
'results' => (int) $wp_query->found_posts,
'created' => current_time('mysql', true),
]);
});
It stores no IP, no user and no cookie. Just what was searched and how many results came back. And since it runs on your server, it counts every search, whether or not the visitor accepted the banner.
Leave it for a week and ask your database what GA can't answer:
-- Searches returning zero, most repeated first
SELECT term, COUNT(*) AS times
FROM wp_wr_searches
WHERE results = 0
AND created >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY term
ORDER BY times DESC
LIMIT 50;
-- Share of your searches that return zero
SELECT
COUNT(*) AS searches,
SUM(results = 0) AS zero,
ROUND(100 * SUM(results = 0) / COUNT(*), 1) AS pct_zero
FROM wp_wr_searches
WHERE created >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY);
(Replace wp_ with your table prefix if it's different.)
The first list is your task for the week. Every repeated term is one of three things: a product you don't stock that people want, a product you do stock under another name, or a typo your search doesn't forgive. All three can be fixed.
If you also want it in Google Analytics
If you live in GA and want the result count there, add this to the same file. It pushes an event to Tag Manager with the term and the results:
add_action('wp_footer', function () {
if (!is_search()) {
return;
}
global $wp_query;
printf(
'<script>window.dataLayer=window.dataLayer||[];dataLayer.push(%s);</script>',
wp_json_encode([
'event' => 'store_search',
'search_term' => get_search_query(false),
'search_results' => (int) $wp_query->found_posts,
])
);
});
In Tag Manager, create a GA4 event tag that fires on the custom event store_search and sends search_term and search_results as parameters. Then register search_results as a custom dimension in GA4 so you can filter by it. Bear in mind this is still subject to consent: what GA gives you is the share of visitors who accepted.
If your search doesn't change the URL
Then the mu-plugin above won't help: your search never goes through the WordPress search page. You have two paths:
- Check whether your search plugin already keeps its own stats. Many do and nobody opens that screen. Look in its menu for something like "Analytics", "Statistics" or "Search log", and check specifically whether it shows searches with no results.
- If it doesn't, ask the vendor how to get notified of each search with its result count. If there's no way, you know you're flying blind on the most valuable part of your store.
In order, here's what I'd do:
- Check whether your search changes the URL. Search for something in your store and look at the address bar. If no
?s=appears, Google Analytics isn't seeing your searches. - Paste the logging mu-plugin (or open your search plugin's stats).
- After a week, pull the zero-result list. It's the list of things your customers asked you for and didn't get.
- Fix the top five. Each one is a product, a synonym or a typo.
GA is a good tool for knowing where people come from. It isn't built to know what they wanted when they arrived, and in a store that's what sells. If you want to see why search is a store's most direct data source, there's the guide on how to choose an ecommerce search, with every option's pricing checked. And the numbers in this article come from WildRock, which logs every search, its results and whether it ended in an order.
Is your store losing sales to searches that find nothing?
WildRock adds a search to your WooCommerce that understands what shoppers mean, and tells you in euros how much it generates. Free plugin, no code.
Keep reading
Synonyms in WooCommerce search: 495 searches returning zero because they were spelled differently
"blue lock" finds 22 products; "bluelock", zero. Measured today on a real store: six words customers write differently from the catalogue added up to 495 searches in 68 days, and the store had zero synonyms configured. How to find yours and fix them today.
Abandoned carts in WooCommerce: what actually gets recovered
Measured on a real WooCommerce store: the median time from search to order is 7.5 minutes, and 90% of orders land within the first hour. The abandoned-cart email arrives after the decision is made. Here is where the money is really lost, and how to read the carts already sitting in your database today.
