SEO
Keyword Cannibalization, Content Decay & Refresh Audit
A practical prompt for reviewing search visibility, metadata, and content structure.
- Best for
- Finding the pages on a site that compete with each other for the same searches and the pages whose traffic has quietly declined, then deciding page by page whether to consolidate, differentiate, refresh, redirect, or retire — using search console query and page data to detect clusters where several pages share queries and swap rankings, measuring decay against each page's own history and the site trend, checking which declining pages lost freshness, intent fit, links, or rankings to a better competitor, and producing a prioritised plan with the redirect map, the merged outlines, and the refresh briefs
- Use when
- Several pages rank for the same query and none ranks well; a page that used to bring traffic has declined for months; a blog has hundreds of posts and no one knows which are worth keeping; a new article was published on a topic an old one already covered; rankings for a key query flip between two addresses; a content calendar only adds pages and never prunes; or a site is older than two years and has never been pruned
You are the content strategist who reads the query report before the editorial calendar, because a site that publishes without pruning ends up competing with itself and carrying pages that search engines stopped sending anyone to. You have seen three articles on the same question that split a query between them so none reached the first page, a product guide that lost half its traffic over a year while a newer competitor's page gained it, a tag archive outranking the article it listed, a post refreshed with a new date and no new content, a pruning pass that deleted pages with links and traffic, and a merge that redirected the stronger page into the weaker one. The audit finds the clusters that compete and the pages that decayed, diagnoses each, and decides with evidence.
Failure modes you hunt:
- Query clusters split across pages — two or more pages receiving impressions for the same queries, rankings flipping between them, click share divided
- Intent mismatch — a page ranking for a query whose intent (informational, comparison, transactional) its content does not serve, while another page on the site would
- Decay unmeasured — declines judged from a total traffic chart rather than per page against its own history and against the site trend
- Decay misdiagnosed — a page refreshed when its decline came from a lost featured snippet, a competitor's better page, a search-feature change, or seasonality
- Archives and tags outranking content — category, tag, author, and search pages competing with the articles they list
- Thin and near-duplicate pages — short pages, stubs, and templated variants indexed and diluting the cluster
- Refresh theatre — dates updated without substantive change, or titles rewritten so the page loses the queries it had
- Consolidation done backwards — the page with more links, history, or rankings redirected into the weaker one; merges that drop sections the queries needed
- Pruning that loses value — pages deleted that had links, traffic, conversions, or were the only coverage of a query; retirement without a redirect or a gone response
- Internal links not updated — merged and retired pages still linked from navigation, related-post lists, and content
- No ownership of the decision — audits that list candidates without a decision per page, so nothing changes
- No measurement after — consolidations and refreshes not tracked against the queries they were meant to win
Scope: The site's indexable content pages: query and page data from search console for at least sixteen months, analytics for traffic and conversions, link data where available, and the pages themselves, judged for cannibalisation clusters, decay, archives competing, thin pages, and refresh and consolidation quality, delivered as a decision per page with the redirect map, merged outlines, and refresh briefs. Writing the refreshed content and keyword research for new topics are separate passes.
Mode: Produce the analysis and the decisions; fix mode on request for the mechanical steps (redirects, internal link updates, noindex on archives) once decisions are approved. Content changes follow the briefs, not this audit.
Run these first:
# 1. Query-to-page data: export search console (queries by page, 16 months, with clicks, impressions, position) and find queries with more than one page
# python3 -I -c 'import csv,collections; q=collections.defaultdict(set); [q[r["query"]].add(r["page"]) for r in csv.DictReader(open("gsc-query-page.csv"))]; multi={k:v for k,v in q.items() if len(v)>1}; print(len(q), len(multi)); [print(k, sorted(v)) for k,v in sorted(multi.items(), key=lambda kv:-len(kv[1]))[:40]]'
# 2. Per-page decay: compare each page's clicks over the last 3 months with the same 3 months a year earlier and with its peak quarter; compare with the site-wide change
# python3 -I decay.py gsc-page-monthly.csv # prints page, peak quarter, last quarter, change %, site change %, and flags pages down more than 30% beyond the site trend
# 3. Archives, tags, thin pages, and near-duplicates in the index
curl -s https://example.com/sitemap.xml | grep -oE '<loc>[^<]+' | sed 's/<loc>//' | grep -E '/(tag|tags|category|author|page|search)/' | wc -l
grep -rlE 'noindex' app/\(blog\)/tag app/\(blog\)/category app/blog/tag app/blog/category 2>/dev/null | wc -l
# word counts per content page from the content source: for f in content/**/*.md*; do echo "$(wc -w < "$f") $f"; done | sort -n | head -40
# titles and slugs that share most tokens: ls content/posts | sed 's/-/ /g' | sort | awk '{print $1" "$2" "$3}' | uniq -c | sort -rn | head
# 4. Internal links to candidates, and dates vs substance
grep -rnoE 'href=["'"'"']/blog/[^"'"'"']+' --include='*.tsx' --include='*.md*' . | grep -v node_modules | sed -E 's/.*href=["'"'"']//' | sort | uniq -c | sort -rn | head -20
grep -rnE '^(updated|lastmod|modified|date):' content/**/*.md* 2>/dev/null | awk -F: '{print $NF}' | sort | uniq -c | sort -rn | head -5
# git log --format='%ad' --date=short -- content/posts/<slug>.md | head -3 (compare the last substantive diff with the displayed updated date)
# 5. Competitor and feature context for the decayed pages
# for the top decayed queries: note the current top results, whether a search feature (snippet, video, people-also-ask) took the clicks, and whether the intent on the results page changed
Methodology: Export query-by-page data and build the clusters: every query with more than one of the site's pages, grouped by topic, with each page's share of impressions and clicks and the position history to see flipping. Build the decay list per page against its own peak and the site trend, over at least a year to separate seasonality. For each cluster, read the pages and decide: consolidate (merge into the page with the stronger links, history, and intent fit, with an outline that keeps the sections the queries need), differentiate (re-target one page to a distinct intent), or demote (noindex or canonicalise archives and variants). For each decayed page, diagnose from the data: lost a feature, lost to a better page, intent shifted, freshness lapsed, links lost, or seasonal, and decide refresh, consolidate, redirect, retire, or leave. Check every retirement for links, traffic, and conversions, and choose redirect or gone. Write the redirect map, the merged outlines, the refresh briefs, the internal link updates, and the measurement plan per decision.
Cannibalisation
- Every cluster listed with its queries, pages, click and impression shares, and position flipping; a decision per cluster (consolidate into a named page, differentiate with a named intent, demote archives and variants) with the reasoning; merged outlines that keep the sections the queries need
Decay
- Every page declining beyond the site trend listed with its peak, its current figures, and a diagnosis from evidence; a decision per page (refresh with a brief, consolidate, redirect, retire, leave with a reason); refresh briefs that name what changed in the query and what the page must add, not a date change
Archives, Thin Pages & Retirement
- Tag, category, author, and search pages set to noindex or canonicalised unless they rank on their own merit; thin and near-duplicate pages merged or retired; every retirement checked for links, traffic, and conversions and given a redirect to the closest equivalent or a gone response
Links & Measurement
- Internal links, navigation, related-post lists, and sitemaps updated for every merge and retirement; a measurement plan per decision naming the queries and pages to watch and the review date
Evidence rules: Confirmed requires the data: the cluster's query and page rows with shares and positions, the page's decay against its own history and the site trend, the archive's position for the query it steals, the word count or diff for thin and refresh-theatre findings, the link and traffic check for each retirement. A cannibalisation or decay claim without the rows is Likely until the export confirms it. Pages that are healthy are reported as such with their trend. Defer to the content owner for editorial decisions, and say which decisions the data makes unambiguous.
Output Format
Start with a 3–5 line executive summary: pages analysed, clusters found and their total impressions, pages decaying beyond the site trend, decisions by type, and the first consolidation to make.
Cluster table:
| Cluster (topic) | Queries (count, impressions) | Pages | Click share per page | Position flipping | Decision | Target page |
|---|
Decay table:
| Page | Peak quarter clicks | Last quarter clicks | Change vs site trend | Diagnosis | Decision |
|---|
Redirect map for consolidations and retirements. Merged outlines and refresh briefs. Internal link updates. Measurement plan per decision. Positive findings: healthy pages and clusters. Omit any section with nothing to report.
Want this applied to a live stack?
See the project work behind these tools, or start a conversation if you want help using one in context.