Detection Tactics and Fixes for Keyword Cannibalization
When Two Pages Are Stealing Traffic from Each Other
This is how I discovered my own keyword cannibalization mess—by accident, and kind of in a rage. I was checking Google Search Console to figure out why an AdSense RPM winner from three months ago suddenly hit the floor. Turns out I had two similar blog posts, both trying to rank for the exact same phrase: “fix Chrome devtools network throttling bug.”
The older article had a bunch of backlinks but was now being outranked by the newer one, which had better structure (thanks to me finally using semantic HTML5… go figure). So both were ranking mediocrely. Neither had the top spot anymore. That’s how I fell into the rabbit hole of keyword cannibalization—not because of some SEO alarm or notification, but because I felt like my content was self-sabotaging. And it was.
Spotting Cannibalization in Google Search Console
There’s no clean dashboard or button in GSC labeled “Keyword Cannibalization.” You’ve got to cross-check manually and watch for patterns that shouldn’t exist. Start with the Performance section → Queries → then filter by one keyword you care about. Scroll and note how many different URLs are listed under that keyword.
Warning sign: If two or more URLs are getting impressions but fighting over the position (e.g., one is rank 11, the other 12, and they flip weekly), that’s a classic cannibalization signal.
It’s misleading because both URLs are technically “ranking” — but the shared weight between them keeps either from rising properly. Especially if one is evergreen and the other is a one-off tutorial you forgot about writing during some caffeine bender at 3am.
The Stupid Ways This Happens Automatically
Some of the worst cannibalization problems happen quietly when:
- You auto-generate meta titles with the same prefix or format
- Tags and categories on WordPress create indexable near-duplicate pages
- Pagination or infinite scroll has poor crawl logic (Googlebot hits 8 versions of “best static site generators”)
- You change URL structures, but the old ones remain in sitemap.xml
- AI-assisted rewrites spit out accidentally identical headers/subheads
I once had three pages targeting “JavaScript memory leak,” all because I was testing which topic titles performed better on social media. Totally forgot those test posts were still indexable. They ranked like garbage. But together, they managed to be worse than just one post done decently.
Using Ahrefs or Semrush to Cross-Check Conflicts
Both Ahrefs and Semrush make this slightly easier, although their terminology differs. Ahrefs calls it the “Competing Pages” report. You plug in your keyword or URL, and it’ll show if multiple pages from your domain are popping up for the same term.
If you use these tools, look at:
- Pages with similar ranking positions (meaning one didn’t totally win)
- High impression count but low CTR across multiple URLs
- Fluctuating keyword positions across those pages over time
What’s ironic—and this confused me at first—is that the winning page in Ahrefs might be showing a higher rank aggregated, but GSC might still show clicks going elsewhere. That’s not a bug. That’s Google behaving like a chaos algorithm depending on context and device type.
Canonical Tags Don’t Always Save You
This tripped me up hard: I had what I thought was a clean canonical tag setup. The older article had a <link rel="canonical" href="/main-guide">
pointing to the newer, better version. Guess what? Google ignored it.
Why? Because they don’t always respect canonicals, especially if:
- The canonical page is slightly thinner in content
- Internal linking is still favoring the old page
- The old page has stronger backlinks
So even with the canonical in place, both URLs were still indexed, still ranking, and still splitting keyword equity. Felt like I was losing an argument with myself.
One weird behavior I noticed in the logs: Googlebot visited the canonical-tagged page 3x more frequently than the target. Like it didn’t believe me.
Fixed It With a Merge + Redirect Combo
Eventually what actually worked: I merged the content (old post’s demo, new post’s updated code snippets), 301 redirected the old slug to the new one, and rewired internal links across my blog. Within two weeks, the cannibalized pair was gone, and the surviving URL actually climbed three spots just from that change.
I also inserted a post-wide signal at the top:
<meta name="robots" content="noindex">
on the outdated post before redirecting. Don’t do both in the same deploy — or Google might shadowban the new one thinking the redirect is sketchy. (Seen that happen. It’s weird and undocumented.)
How to Choose the “Survivor” Page
Sometimes it’s not obvious which page should win – especially if one’s structured better and the other ranks better. Here’s the rough benchmark I use when cleaning up a mess:
- Better backlinks: Keep the one with external reference juice
- Freshness: If one was updated this year, save that one
- Internal links: Less work if the more-linked version lives
- URL readability: Shorter, no dates, and keyword clean? Keep it
- AdSense CTR: Whichever page naturally drives better ad unit behavior
Yeah, that last one’s not typical SEO advice. But when you’ve seen how small layout quirks affect a sticky sidebar ad’s viewability percentage, you start factoring that stuff into content strategy.
You Can’t Always Rely on sitemaps.xml Either
This is the part where I stopped trusting automatic sitemap generators entirely. Screaming Frog was flagging two near-duplicate URLs in my Yoast-generated sitemap from an old taxonomy template I forgot even had posts attached. They were garbage SEO-wise but still crawled and ranked (poorly), stealing maybe 10% of visibility.
Here’s the thing: Google isn’t always crawling your preferred URLs just because you sanitize your sitemap. If even one orphaned internal link exists pointing to those zombie pages—from somewhere like a tag archive? They get crawled. And if you target the same keywords? Boom: cannibal conflict.
Actual quote from a Search Console URL inspection: “URL is not on Google: alternate page with proper canonical tag.” But it was on Google. Cached and everything.
How AdSense Reports Can Accidentally Flag It Before Search Console Does
This was such a dumb but useful signal: My AdSense coverage report started showing declining matched content clicks for an article that, until then, had always converted strong. No changes made. But it was down about thirty to forty percent… and it wasn’t seasonal. I checked traffic: still solid. But my RPMs were getting diluted because Google was splitting impressions between two similar articles.
AdSense doesn’t say “you have keyword cannibalization,” obviously. But a sudden change in RPM with no traffic drop? That’s AdSense telling you the keyword targeting broke—even if it doesn’t know what happened. When that happens, go back to Search Console, then over to Coverage, and inspect those dips. It’s often a page cannibalizing from another in the same vertical.
The Unfixable Edge Case: Multilingual Posts Hitting Same Keyword Stem
This one’s not in any doc I’ve seen. If you have multilingual pages running under hreflang and you recycle titles (e.g., “How to set up Cloudflare DNS” in EN and ES), Google sometimes conflates the two in search position calculations. I ran into this with a client’s subdomain setup—en.site.com
and es.site.com
were both ranking for the English query because both included the same meta title format.
Weirdest part? The Spanish one wasn’t even indexed on google.com, but its canonical page was still interfering in rankings. Again: this doesn’t happen all the time. But in regions where ranking volatility is high (Canada, Switzerland, parts of Mexico), you might see this unintentionally due to mixed regional targeting. If that happens: isolate country targeting in GSC and be explicit with hreflang AND canonical.