calendar Last updated: 19 August 2026
How I Catch Content Cannibalization with Claude
Data integration

How I Catch Content Cannibalization with Claude

Try It Free

Connect your data in 1 min. Free forever plan

Got insights from this post? Give it a boost by sharing with others!

Search Console, GA4, and your CMS (WordPress here, since that’s what we run) are usually three separate places to check: three logins, three exports, three URL formats that don’t match by default. Joined into one table, keyed on the same URL, they stop being three exports and become a single queryable dataset, a real dashboard you can ask questions of.

Two examples of what that makes visible: pages competing with each other for the same queries, and updates whose results get judged before Google has even had a chance to see them.

Both patterns show up on any site that’s had more than one person touching content over time. Strategy shifts, priorities change, different people make different calls without a shared view of what’s already live, that’s what causes them, not a one-off mistake.

I check for both with the cannibalization module in an internal dashboard built around a process I ran by hand for years before it existed: pulling Search Console, GA4, and WordPress data separately and merging it myself in spreadsheets. The dashboard covers a lot more than these two problems, editorial priorities, opportunity scoring, page-level performance, but here’s how I use it for these two specifically. Making a call between two competing pages takes three sources, each doing a different job:

  • Search Console shows which one Google already prefers, and when it last crawled either.
  • WordPress shows when each was last edited, so you know whether a before/after read is even trustworthy yet.
  • GA4 shows the engagement rate for each, telling me whether the page I’m keeping needs anything from the one I’m redirecting.

Here’s a pair that came up when I ran this check.

The pair I found

Eight shared queries. One page pulling 38% of its own impressions from that shared set, the other 100%.

URLs and queries below are relabeled, real pages, real numbers, just not ones I’m naming publicly:

  • 38.0% of Page A’s impressions come from these 8 queries
  • 100.0% of Page B’s impressions come from these 8 queries
Query Page A impr. Page A pos. Page B impr. Page B pos.
Query 1 668 3.0 44 3.0
Query 2 26 26.0 46 6.2
Query 3 69 7.9 1 13.0
Query 4 35 6.7 33 9.6
Query 5 17 8.0 2 9.5
Query 6 11 7.2 7 10.7
Query 7 8 9.0 1 7.0
Query 8 4 77.8 3 88.3

Keep Page A. It averages position 4.8 on the shared queries versus 8.1 for Page B, Google already prefers it. Page B should be merged into Page A and redirected, it draws no impressions from outside this overlap.

Unique to Page A: 1,367 impressions outside the shared set. Unique to Page B: none of significant volume.

Page B has no traffic outside the shared queries, and Page A outranks it on every one of them. That’s not visible from either page’s Search Console numbers alone, only when you check what percentage of each page’s traffic comes from the shared set, a percentage Search Console doesn’t calculate for you.

Contrast this with a pair where the overlap is real but partial, say 40% for one page and 20% for the other. Redirecting there is riskier: both pages draw meaningful traffic the other doesn’t touch, so consolidating would throw away real, unique impressions. Same range of shared queries, a different correct action, and you can’t tell which situation you’re in without the percentage.

Before I redirect anything, I check GA4

Search Console only measures visibility, not what happens after the click. If the losing page has better engagement metrics, the fix is updating the winner’s format to match it, not just redirecting.

So before I acted on the recommendation above, I pulled GA4 engagement data for both pages over the same window:

Metric (last 3 months) Page A Page B
Sessions 53 10
Engagement rate 47.2% 50.0%
Avg. engagement time 31s 22s

Nothing here changes how Page A ends up looking. Page B’s engagement rate edges out Page A’s by under 3 points, but that’s on a sample of 10 sessions, too small to treat as a real difference. Its average engagement time is actually shorter, not longer. If Page B had real volume behind that engagement rate, or if its engagement time were ahead too, that’s the pattern that would send me back into Page A’s content to borrow whatever Page B was doing right, before touching the redirect. Here, there’s nothing worth carrying over.

That’s the typical outcome: most of the time, GA4 doesn’t turn up anything worth changing about the page I’m keeping. I run the check every time regardless, because the times it does are exactly the times I’d want to update that page’s format before redirecting, not just after.

Why I don’t trust a before/after check without a crawl date

The common way to measure a content update: change the page, note the date, come back in a couple weeks, compare Search Console numbers before and after. It’s also an easy way to reach the wrong conclusion.

Search Console’s Performance data doesn’t record when Google recrawled a page, only when clicks and impressions were logged. You can get a page’s last crawl date from the URL Inspection tool in the GSC UI, but only one URL at a time, and there’s no way to export it, you’d have to check and note down each URL by hand. Confirming it at any scale means the URL Inspection API instead.

So before trusting a before/after comparison, I check whether Google has recrawled the page since I edited it. If not, I stop. The dashboard also flags this automatically: any edit inside the last 14 days is too fresh to read, on top of Search Console’s 2-3 day reporting delay and GA4 dropping the current day as partial.

New content needs the same check, in reverse. The data point is when the new page’s URL first shows impressions in Search Console, not the publish date. Zero impressions for the first couple of weeks may just mean it’s not indexed yet, not that it’s underperforming.

What I check, every time

On every page pair, whether or not anything looks off:

  • Any pair where one page draws 80%+ of its own impressions from queries the other page already outranks it on gets flagged for a merge or redirect decision.
  • Position gap gets checked on the shared queries specifically, not each page’s overall average position, which is diluted by every unrelated query it also ranks for.
  • Before acting on that flag, I pull GA4 engagement rate and average engagement time for both pages. If the “losing” page is ahead on both, with real session volume behind it, I update the winner’s format to match it before redirecting.
  • Pairs with real, meaningful traffic unique to both pages get a closer look, not a redirect: I check whether the overlapping queries are actually important to each page on its own terms, and how much of each page’s unique impressions they’re pulling in, before deciding whether the two need to be differentiated or can stay as they are.

How I ask for it instead of exporting it

Once this is one connected dataset, I ask directly instead of exporting three things and eyeballing them:

Which of our page pairs have one page drawing 80%+ of its own impressions from non-branded queries it shares with another page, each query with a minimum of 30 impressions over the last 30 days? Doesn't matter if that's over 3 shared queries or 30.
Pull GA4 engagement rate, average engagement time, and sessions for [url] and [url]. Flag it if either is ahead on both engagement metrics.
Has Google recrawled [url] since I last edited it? If not, don't compare before/after numbers yet, tell me when it's safe to check.
Give me our top 10 cannibalized page pairs, ranked by overlap percentage. For each, show how much traffic each page gets, last crawl date, publish date, and engagement metrics for both.

These are ordinary questions to ask once the sources are connected. What made them hard before was three separate logins and three incompatible URL formats to reconcile before the analysis could even start.

🚀 See what your own Search Console, GA4, and WordPress data looks like connected in one place. Free forever plan, no credit card. Try Windsor.ai.

What I’m doing about this pair

Before redirecting, I check whether Page B has any content Page A doesn’t, something worth merging in rather than losing. Here it doesn’t: everything Page B ranks for, Page A already ranks for too, and outranks it on average. Page A keeps 1,367 impressions from outside the overlap. Page B’s contribution outside the overlap rounds to nothing, nothing meaningful to lose by redirecting it in.

Conclusion

Cannibalization and measuring too early are both common problems. Both stay hidden unless you’re looking at Search Console, GA4, and WordPress together.

I’ve built this kind of dashboard before, more than once, at previous jobs. Every time, step one was authenticating each source myself, a few minutes per connector, and it doesn’t always go smoothly. What’s different about doing it through a connector is what happens after that: I can share the dashboard, or a Claude skill built on top of it, with a teammate without handing them an API key, or with anyone who already has the same connectors set up on their own end.

The tools behind this

I pull this from our own Google Search Console, Google Analytics 4, and WordPress connectors. If you also want this queryable from Claude, here’s the Windsor MCP setup guide.

FAQs

Is this a real example or a made-up one?

Real, pulled from our own internal dashboard against windsor.ai’s own Search Console and GA4 data. URLs and queries are relabeled Page A/B and Query 1-8 so this doesn’t expose our keyword targeting.

Tired of juggling fragmented data? Get started with Windsor.ai today to create a single source of truth

Let us help you automate data integration and AI-driven insights, so you can focus on what matters—growth strategy.
g logo
fb logo
big query data
youtube logo
power logo
looker logo