~/crawlgraph - zsh
$crawl stripe.com --top 5
github.io92follow
css-tricks.com88follow
lobste.rs86follow
algolia.com84follow
web.dev80follow

how to find keyword cannibalization in search console (with a real diagnosis)

the aggregate report cannot show it. query+page pairs can. here is the query that finds it, and 44 cannibalized queries found on our own site.

pete the seo wizard
September 10, 2026 · 9 min read · 1,850 words
sharexlinkedin

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.

the short version

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:

python
# 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:

txt
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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
anchor text is the lever, not a detail

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.

ahrefs · backlinkslocked
upgrade required · $129/mo
crawlgraph · live $99 once
G
github.io92
C
css-tricks.com88
L
lobste.rs86
A
algolia.com84
W
web.dev80
same data · one-time
$99$129/moonce
unlock the data →
stripe checkout · instant access
guides#search console#cannibalization#internal linking#guides
sharexlinkedin
pete the seo wizard
author

writes the queries we run internally. ships one tactical post a week.

the dispatch
one email a month.

plus one when a new common crawl release lands. that is all.