Keyword cannibalization is what happens when two or more pages on a client's site compete for the same query and the same search intent, and Google can't decide which one to rank. You see it as flip-flopping URLs in Search Console, a page stuck at position 11-14 for months, or a blog post outranking the service page that's supposed to convert.
It's one of the most common findings in an agency technical audit, and also one of the most over-diagnosed. Half the "cannibalization" in audit decks is just a site having multiple pages that mention a keyword. That's normal. Real cannibalization requires overlapping intent plus measurable competition in the SERP.
Here's the workflow we'd run on a client site, start to finish.
What keyword cannibalization actually looks like
Three signals, and you want at least two before you act:
- URL swapping over time. In Search Console, the page ranking for "commercial hvac maintenance" is /services/hvac-maintenance in March, /blog/hvac-maintenance-guide in April, back to the service page in May. Google is uncertain.
- Split impressions on one query. Two URLs each pulling 400-900 impressions for the same query, both sitting at position 9-15, neither breaking through.
- Intent mismatch in the winner. The informational blog post ranks and the money page doesn't. Conversions drop even if sessions look fine.
What is not cannibalization: a category page and a product page both ranking for a broad term (that's often a double listing and a win). A location page and a service page ranking for different modifiers. Two posts that mention the same term but target genuinely different questions.
The intent test
Before you merge anything, ask: could one page satisfy every searcher who types this query? If "seo audit" searchers want both a definition and a service, you may need two pages with clearly separated targeting. If they all want the same thing and you have three pages trying to give it to them, that's cannibalization.
How to find keyword cannibalization: the GSC export method
You don't need a dedicated tool. You need a 6-month Search Console export and 30 minutes.
- Export queries + pages. Use the Search Console API, Looker Studio, or a bulk export. You want rows of query / page / clicks / impressions / position over 6 months (not 3 — you need enough history to see swapping).
- Filter to queries with 100+ impressions. Below that you're chasing noise. On a mid-size client site this usually cuts 40,000 rows to about 1,200.
- Group by query, count distinct URLs. Pivot table. Flag any query where 2+ URLs have at least 10% of that query's total impressions.
- Add a month dimension and check for swapping. For each flagged query, see whether the top-impression URL changes month to month. If it does, that's your highest-priority fix.
- Sort by commercial value, not volume. A cannibalized query with 300 searches and buying intent beats one with 8,000 informational searches.
A second pass: run a site:-style check in your rank tracker for the client's top 20 target keywords and look at which URL is ranking. If it isn't the URL you assigned in your content map, you have a targeting problem even if impressions look clean.
The audit spreadsheet columns that matter
For each cannibalization candidate, capture: query, competing URLs, impressions per URL, best position per URL, referring domains per URL, conversions per URL (last 90 days), publish date, and your recommended action. That last column forces a decision instead of a vague "review these pages" line item in the deck.
On client work we keep this as a tracked deliverable with owners and due dates rather than a loose spreadsheet — the same way you'd handle any phased SEO project with proof attached. Cannibalization fixes span content, dev, and QA, so they stall when nobody owns the handoff.
Picking the winner page
This is where agencies waste the most time. Use a ranked tiebreaker instead of debating:
- Backlinks. The page with meaningful referring domains almost always wins — authority is the hardest thing to transfer cleanly.
- Conversions. If one page converts at 2.1% and the other at 0.3%, keep the converter even if it has fewer sessions.
- Intent fit. Does the page format match what's actually ranking in the SERP? If the top 10 are all service pages, don't crown a blog post.
- Existing rank stability. The URL Google has picked most often over six months has a head start.
- URL quality. Shorter, cleaner, in the right directory. The weakest tiebreaker — never let it override backlinks.
Write the winner down. Everyone on the account team needs to know which URL owns which query, or you'll re-create the problem in three months with a new blog post.
The four fixes (and when to use each)
1. Consolidate and 301
The default. Move the genuinely unique sections from the loser into the winner, then 301 the loser to the winner. Expect the winner to wobble for 2-3 weeks before settling. In a typical mid-size consolidation — merging three 900-word posts into one 2,100-word page — we've seen position go from an average of 12.4 to 5-7 within six weeks, with combined clicks up 60-120%.
Do not merge mechanically. If you stack three posts end-to-end you get a bloated page that matches the query worse than either original.
2. Re-target and differentiate
Use this when both pages have real value for different intents that got blurred. Rewrite the title, H1, intro, and internal anchor text of one page to clearly target an adjacent query. Example: /blog/local-seo-guide keeps "what is local seo," while /services/local-seo gets rewritten around "local seo agency" and "local seo services." Then fix the internal links pointing at each.
This is the right call for multi-location and service-area sites where templated pages blur together — see our guide to location pages that rank without doorway spam for the differentiation patterns that hold up.
3. Canonicalize
Reserve this for near-duplicates you can't remove — faceted URLs, print versions, paginated variants, syndicated copies. A canonical is a hint, not a directive, so don't use it to paper over two genuinely different pages targeting the same term. Google will frequently ignore it and keep swapping.
4. Prune
If a competing page has no links, no conversions, under 50 clicks in 12 months, and nothing worth merging — delete it and return 410, or 301 it to the closest relevant parent. Pruning 15-40 thin posts on a six-year-old blog is routine and often lifts the surviving pages. Just document every removal; clients panic when pages vanish without a log.
Fix the internal links or it comes back
Consolidation fails when internal links still point at the old target. Crawl the site, find every internal link whose anchor text contains the target query, and point it at the winner. Pay special attention to:
- Navigation and footer links (highest weight, most often forgotten)
- Older blog posts with in-content links to the retired URL
- Author bios, related-post modules, and sidebar widgets
- The XML sitemap — remove dead URLs, don't let them linger for months
Then set a rule: one primary query per URL, logged in the content map, checked before any new brief goes out. Prevention costs ten minutes per brief; the cure costs a sprint.
Reporting it to the client without scaring them
"We're deleting 22 pages" lands badly. "We're consolidating 22 underperforming pages into 7 stronger ones to stop them competing with each other" lands fine. Show the before/after on a specific query: two URLs at positions 11 and 16 with 340 combined clicks, becoming one URL at position 4 with 980 clicks.
Set the expectation up front that there's a 2-4 week dip while Google reprocesses. If you don't warn them, the dip becomes a crisis call. Our approach to proving SEO results with before/after data works well here — screenshot the GSC query view the day before you ship and again at 30 and 60 days.
Cannibalization work also pairs naturally with a content decay audit, since you're already in the same export. Run both in one pass and you get a single consolidated content roadmap instead of two overlapping deliverables.
Keyword cannibalization fix checklist
- Export 6 months of GSC query + page data; filter to 100+ impressions

Nick Quirk

