The audit ran against the live dev server at localhost:3011, using the /site-audit-on-page-seo skill in site-wide-technical mode plus a production sanity pass. The brief was direct: get ukcalculator.uk indexed and ranking. Start with the technical blockers.
Within the first sweep it surfaced the pattern. Routes with an NN- prefix in their slugs had near-identical siblings without it. Eight pairs. Each covering the same search intent from two different URLs. Google would see both, be unable to determine which was canonical, and split whatever ranking signal existed between them. Neither page would rank for the intent it was supposed to own.
That was Phase 1.3’s job: kill the duplication before it cost anything worth recovering from.
What the problem actually was
Keyword cannibalisation is usually framed as two different pages chasing the same topic. This was more mechanical. The NN- prefix was a routing artefact: a naming pattern from earlier in the build that had made it into production slugs unchanged. The canonical page for a given calculator should have one URL. It had two. The second didn’t add content; it split ranking signal.
For a calculator site, the entire model is inventory: the breadth of tools and the organic traffic that finds them. Eight cannibalised pairs is eight ranking opportunities pointing nowhere. The fix wasn’t complicated. Strip the prefix, redirect, repoint the internal links. The only question was how to structure the operation so the link graph stayed consistent at every deployment state.
Atomic or nothing
Phase 1.3 landed as one PR. Three groups of file changes, deployed together:
- Routing. 23 routes issued 308 permanent redirects to their canonical unprefixed equivalents.
- Content. Canonical pages updated wherever the prefix had surfaced in headings or metadata.
- Internal link graph. Every MDX cross-link repointed.
The atomic shape wasn’t aesthetic preference. SEO signal is continuous. Googlebot doesn’t pause between your deployments. If routing goes live Monday and internal links update Wednesday, you spend two days with redirects pointing at pages whose own internal cross-links still reference the old URLs. That’s crawl noise you’ve generated yourself: inconsistency in the link graph that reduces Google’s confidence in your canonical choices.
308 rather than 301. The distinction matters: 308 is unambiguous about permanence and handles non-GET methods correctly. Some crawlers treat fresh 301s with scepticism on sites without established crawl history. Permanence needed to be clear from the first crawl.
The devlog marks Phase 1.3 as the largest single ranking move in the locked plan. The verification loop completed with zero soft-failures. Canonical check, redirect resolution, and internal-link audit all clean before merge. That’s the only state worth merging from.
The rest of that evening
Phase 1.3 didn’t land alone. Four PRs merged on 2026-06-14 at 21:24 BST: #99 through #102. The other three were Phase 1 items from the same locked plan, run in sequence the same evening.
Phase 1.1: title double-brand fix. The audit flagged page titles duplicating the brand name. Redundant tokens waste the character budget Google allocates before truncation. Fixing them is a signal-quality improvement, not a visual one. The rendered page looks the same; what Google reads changes.
Phase 1.2: OG images. The site was running without og:image or twitter:image on any route. Phase 1.2 shipped dynamic ImageResponse templates via next/og: two Aether-themed templates with per-article variants. Tier-1 static fallbacks were skipped. next/og works on this stack, produces better output, and doesn’t add a separate asset pipeline to maintain. The session ran straight from 1.1 into 1.2.
Phase 1.5: robots.ts. The static public/robots.txt converted to a Next.js MetadataRoute.Robots export at app/robots.ts. Same policy throughout: User-agent: *, Allow: /, sitemap URL correct. The motivation was consistency. Robots configuration belongs in the same framework-native layer as the rest of the metadata surface, not a flat file that lives outside the build system and is easy to overlook when routes change.
72 hours
Three days after those four PRs merged, Google had started indexing pages. Qualitative confirmation. Not full crawl coverage, not rankings, but active indexation where there had been none before.
For a site with zero indexed pages before Phase 1, this is the expected early signal from a technical foundation fix. Crawl budget starting to flow. The canonical graph coherent enough that Google is choosing pages rather than bouncing between duplicate pairs. The 308 redirects transferred whatever link equity existed on the NN- URLs; for a new site that’s negligible, but the graph is clean for when it accumulates.
Attributing the indexation signal to any single change isn’t possible. All four PRs landed simultaneously. The cannibalisation fix was the largest structural change. The robots.ts conversion ensured the sitemap declaration was served from within the build system. OG images don’t affect crawlability directly. Title fixes affect click-through signal, not crawl indexation. All four were required for the foundation to hold.
What the shape teaches
The atomic PR enforces something easy to undervalue: the verification loop closes at a moment when all three layers are in a consistent state. Routing, content, and internal links checked together, against a system that reflects what production will look like. Verify in pieces, and you verify intermediate states. Those states exist briefly in your deployment sequence but not in the crawl window that follows.
Timing matters independently. Routing artefacts compound. The NN- prefix presumably served a purpose earlier in the build. By the audit, it had produced eight cannibalised pairs across 23 duplicate routes. The longer it sits, the more crawl history accumulates on wrong URLs. Getting it out before any organic traffic had developed on those routes meant the redirect cost was close to zero. Leave it longer and the transition gets messier.
Out of scope: ranking results. Indexation in 72 hours is the technical baseline, confirmation that the crawl blockers are cleared. Rankings are a separate and slower signal, governed by content quality, domain authority, and search intent match. A routing fix clears the way; it doesn’t fill the gap.



