keyword cannibalization is two of your pages competing for one query. the reason it survives for months on most sites is not that it is subtle, it is that the standard Search Console report structurally cannot show it: the query view and the page view are separate, and neither one says "this query is being answered by two of your URLs". you have to ask for both dimensions at once. this post is the query that finds it, and a real diagnosis it produced on this site with the numbers left in.
pull Search Console with dimensions=["query", "page"], group by query, and look at every query that returns two or more of your own URLs. the damage is usually not that both pages rank badly. it is that the better page is the one being shown less.
the query that finds it
the Search Analytics API accepts multiple dimensions in one request. asking for query and page together returns one row per pair, which is the shape that makes a split visible:
# query + page pairs, not the aggregate. this is the whole trick.
rows = client.query(
start_date=start, end_date=end,
dimensions=["query", "page"], # <- both, together
)
by_query = defaultdict(list)
for r in rows:
q, p = r["keys"]
by_query[q].append({"page": p, "impressions": r["impressions"],
"position": round(r["position"], 1)})
# any query answered by 2+ of your own urls is a candidate
split = {q: rs for q, rs in by_query.items() if len(rs) > 1}run that over 28 days and count. on this site, in the 28 days to 7 september 2026, it returned 711 query-page pairs and 44 queries answered by two or more of our own URLs. we had been reading the aggregate for a month and had not seen any of it, because the aggregate cannot contain it.
what a real split looks like
here is the worst one we found. two posts about Moz: a product comparison, and a dedicated explainer about Moz's crawlers. the crawler queries belong to the explainer. google mostly showed the comparison:
query page impr position
--------------------------------- -------------------------- ----- --------
what is moz's crawler called /blog/crawlgraph-vs-moz 87 17.6
/blog/moz-crawler-explained 20 4.3 <-
moz crawled flag /blog/crawlgraph-vs-moz 106 17.5
/blog/moz-crawler-explained 16 6.6 <-
moz site crawl /blog/crawlgraph-vs-moz 232 25.0
/blog/moz-crawler-explained 21 7.6 <-
cancel moz crawl test /blog/crawlgraph-vs-moz 71 13.7
/blog/moz-crawler-explained 3 7.3 <-
read the arrows: whenever google picks the crawler page it ranks it
10-17 positions HIGHER. it just rarely picks it.this is the finding that changes how you think about cannibalization. the naive model is "two pages compete, both get dragged down". what the data shows is sharper and more actionable: google already knows which page is better. it ranks the crawler post at 4.3 and the comparison post at 17.6 for the same query. it simply picks the comparison post five times more often.
so the problem is not that the engine cannot tell them apart. it is that our own site kept voting for the wrong one.
the cause is usually internal linking
when we looked at why, the answer was not in the content at all. the comparison post had a link from the homepage. the crawler post, the one google ranks 13 positions better, had exactly one editorial inbound link on the entire site. every other link pointing at it came from an automated related-posts card whose anchor text was a dump of the article excerpt.
that is the mechanism, and it is worth stating plainly: if two pages can both answer a query, the one your own site links to harder tends to win, whether or not it is the better answer. cannibalization is frequently an internal-linking fact wearing a content costume.
the fix, in the order that matters
- decide which page owns the query. not which is longer or newer. look at the position data: if one page ranks materially better whenever it is shown, that is the engine telling you which one it prefers.
- make the losing page stop competing. move the overlapping phrases out of its body prose and into the anchor text of a link pointing at the winner. the phrase still appears on the page, but now it describes a destination instead of claiming the topic.
- give the winner real inbound links. from pages that genuinely relate, with anchor text that names what the target answers. "read more" is worth nothing here.
- merge only when the intent is identical. if both pages answer the same question, redirect the weaker into the stronger and let it inherit the signals. if they answer different questions, keep both and fix the linking instead.
the same audit found eight of our pages cross-linking each other with the bare brand name as anchor text. a link whose anchor is "SpyFu" tells a search engine nothing about whether the target is a review, a price list, or an alternatives page. changing those to anchors that name the destination is the cheapest cannibalization fix available, because it requires no new content at all.
when it is not a problem
do not go hunting for splits to fix. two URLs on a broad query is normal, and a brand query returning your homepage plus three sections is healthy. the ones worth acting on have at least one of these properties:
- the two pages answer the same question, not adjacent ones
- the page with better positions is the one shown less often
- neither page reaches page one, and their combined impressions suggest one merged page plausibly would
everything else is a site having more than one relevant page, which is what a site is supposed to have.
record the before-state
write down the per-query positions for both URLs before you change anything. recovery here is slow, several weeks is realistic, and ranking data is noisy enough that without a baseline you will not be able to tell a fix from ordinary drift. the same query+page pull is the baseline: run it, keep the json, run it again in a month.
faq
What is keyword cannibalization?
Keyword cannibalization is when two or more pages on the same site compete for the same search query, so a search engine has to choose between them. The cost is rarely that both rank badly. It is that the engine repeatedly picks the weaker page, splits the signals that would have lifted one page onto two, and leaves you ranking worse than either page would alone.
How do I find keyword cannibalization in Google Search Console?
Query the Search Analytics API with the dimensions query and page together, then group the rows by query. Any query returning two or more of your own URLs is a candidate. The Search Console web interface makes this hard to see because its query report and page report are separate views, and the aggregate never reveals that one query is being answered by two pages.
Is keyword cannibalization always a problem?
No. Two pages appearing for a broad query is normal and often good, and a brand query legitimately returns your homepage plus several sections. It becomes a problem when the pages serve the same intent, when the better page consistently ranks below the weaker one, or when neither page reaches page one while a merged page plausibly would.
Should I delete or merge cannibalizing pages?
Merging and redirecting is right when the two pages genuinely answer the same question, because the combined page inherits both sets of signals. Keeping both is right when they answer different questions and the problem is that your own site failed to say which is which. In that case the fix is editorial: make each page commit to its own question, and link between them with anchor text that names the difference.
How long does it take to recover after fixing cannibalization?
Long enough that you should not judge it in a week. The engine has to recrawl the changed pages, re-evaluate which one answers the query, and work through its own ranking latency, so a few weeks is a realistic window before the split resolves. Record the before-state per query first, because without it you cannot tell recovery from ordinary volatility.
writes the queries we run internally. ships one tactical post a week.
plus one when a new common crawl release lands. that is all.