agentleFS
keuperakeupera/seo-skills/skills/keyword-cannibalization/SKILL.md

keyword-cannibalization

Skill0 starsChanged 2 months agoMITView on GitHub

Detect and fix cases where several pages on the same site compete for the same query, splitting signals and suppressing all of them. Use whenever the user mentions keyword cannibalization, pages competing with each other, the wrong page ranking for a query, rankings that keep swapping between URLs, duplicate topics, or has published many posts on similar subjects. Also use when a page won't rank despite being good, or when Search Console shows several URLs alternating for one term.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 6 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    If three of your own pages come back and any of them could plausibly be the answer, that's the finding.

    line 62
  2. Distinctive:

    Not the best-written page.

    line 78
  3. Distinctive:

    Remove the losers from the sitemap, confirm the winner is in it.

    line 96
  4. Distinctive:

    Check external links to the losers.

    line 97
  5. Distinctive:

    Wait four to six weeks before judging the result.

    line 98
  6. Distinctive:

    Check the intent against the live SERP first.

    line 118

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Keyword Cannibalization

Cannibalization is when two or more pages on a site compete for the same query. Google picks one, the signals that should have concentrated on a single strong page are spread across several weaker ones, and the choice often flips week to week.

It's over-diagnosed. Two pages appearing for related queries is normal and healthy. Real cannibalization has specific symptoms, and the fix is usually consolidation, which people resist because it means deleting work.

What it actually looks like

Real cannibalization shows at least two of these:

  • The ranking URL for a query changes repeatedly over weeks, with no content changes.
  • Several URLs get impressions for the same query, each with low CTR and mid-range position.
  • The wrong page ranks — a blog post ranking for a commercial term the product page should own.
  • A page's position improved when another was removed, historically.
  • Two pages answer the same question, and a reader wouldn't know which to read.

What is not cannibalization:

  • Two pages ranking for related but distinct queries. That's coverage.
  • A category and a product page both appearing for a broad term. Different intents.
  • Two pages appearing in the same SERP. Google shows two results from one site regularly, and that's a good outcome, not a problem.
  • A page ranking for a term you didn't target. That's a bonus.

Detection

Group Search Console data by query, count distinct URLs, and look for the queries where several pages get meaningful impressions.

python - <<'PY'
import json, collections
rows = json.load(open('gsc.json'))['rows']   # dimensions: ["query","page"]
by_query = collections.defaultdict(list)
for r in rows:
    q, p = r['keys']
    if r['impressions'] >= 50:
        by_query[q].append((p, r['impressions'], r['position']))

for q, pages in by_query.items():
    if len(pages) < 2:
        continue
    pages.sort(key=lambda x: -x[1])
    top, second = pages[0], pages[1]
    # both getting real impressions, neither clearly winning
    if second[1] > top[1] * 0.25 and min(top[2], second[2]) > 3:
        print(f"\n{q}")
        for p, imp, pos in pages[:4]:
            print(f"   {imp:>6.0f} imp  pos {pos:>5.1f}  {p}")
PY

The other detection method takes thirty seconds and needs no data:

site:example.com "the exact query"

If three of your own pages come back and any of them could plausibly be the answer, that's the finding.

The decision

For each cluster, pick one of four. The choice depends on whether the pages serve genuinely different intents.

Consolidate. The pages answer the same question. Merge into the strongest URL, 301 the others. This is the right answer most of the time and produces the biggest gains.

Differentiate. The pages could serve different intents but currently overlap. Rewrite each to own its intent clearly, and link them to each other. Use when both topics genuinely deserve a page.

Demote. One page should own the term; the others should support it. Rewrite the supporting pages to target their own angle, remove the competing term from their titles and H1s, and link them to the winner with the target term as anchor.

Delete. Thin, no links, no traffic, no purpose. 410 it, or redirect to the winner.

Picking the winner

Not the best-written page. The one with the most accumulated equity, because that's the part you can't rebuild:

  1. Most referring domains
  2. Best current position for the target term
  3. Most organic traffic
  4. Best intent match for the query
  5. Best content

In that order. A mediocre page with 40 backlinks beats an excellent page with none, and the fix is to improve the mediocre page rather than to promote the excellent one and lose the links.

Consolidating properly

Order matters, and skipping step 4 is how consolidations underperform.

  1. Move the genuinely useful sections from the losers into the winner. Rewrite them in; don't concatenate.
  2. Make the winner comprehensively better than any loser was. If it isn't, you've merged two mediocre pages into one.
  3. 301 every loser to the winner. Permanently.
  4. Update every internal link to point at the winner directly, not through the redirect. Internal links pointing at a redirect still work but they're the main reason merged pages don't jump the way people expect.
  5. Remove the losers from the sitemap, confirm the winner is in it.
  6. Check external links to the losers. They now flow through the redirect, which is fine, but it's worth knowing which they were.
  7. Wait four to six weeks before judging the result.

Prevention

Cannibalization is a planning failure, not a writing failure. It's created at the brief stage.

  • Maintain a keyword-to-URL map. One primary query, one page. Check it before commissioning anything.
  • Before writing, search site:example.com for the target term. Thirty seconds, and it catches most of it.
  • Prefer updating an existing page over writing a new one when the topics are close. Usually the better trade anyway. See content-refresh.
  • Resist splitting a topic to hit a page count. Three pages on "how to X", "X best practices" and "X tips" are one page.
  • Keep the map at the level of reader questions, not keyword variations. "Cheap X", "affordable X" and "budget X" are one question.

The commercial version

The most expensive form of this is a blog post outranking the product or category page for a commercial term. The post gets the traffic and converts nobody.

Fix by clarifying roles rather than deleting anything:

  • The commercial page owns the commercial term. Strengthen it, get internal links pointing at it with that anchor text, make sure it can actually answer the query rather than just selling.
  • The blog post targets the informational angle, drops the commercial term from its title and H1, and links to the commercial page prominently with the commercial term as anchor.
  • Check the intent against the live SERP first. If the results for that term are all blog posts, the post ranking is correct and the product page was never going to.

Where the data comes from

Detection needs query-level data with URLs attached, and Search Console provides it free. That's enough for everything above on a site you own.

Where it falls short is deciding between differentiate and consolidate, which depends on whether the two terms genuinely have separate intent and separate demand. Keupera covers the intent classification and volume side alongside the SERP composition, over MCP, which is useful when the call isn't obvious from the SERP alone:

claude mcp add --transport http keupera https://mcp.keupera.com/mcp -s user

Related skills

search-console-analysis, content-refresh, topical-authority-map, internal-linking-architecture, canonical-duplicate-content, keyword-research-briefs

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.