A Stale Domain Was Serving My Whole Site — How I Found and Killed a Duplicate-Canonical Crisis
A Stale Domain Was Serving My Whole Site — How I Found and Killed a Duplicate-Canonical Crisis Last week Google Search Console told me something alarming: several of my pages were flagged "Duplicate, without user-selec
A Stale Domain Was Serving My Whole Site — How I Found and Killed a Duplicate-Canonical Crisis
Last week Google Search Console told me something alarming: several of my pages were flagged "Duplicate, without user-selected canonical." Google had found duplicate copies of my content — and I hadn't published duplicates anywhere. Or so I thought.
The crime scene
My site is NazmHub (https://nazmhub.pages.dev), a free bilingual (Urdu + English) writing platform I built and run solo. Months ago, during early deploys, the hosting platform had auto-assigned the project a different subdomain — something like qissa-37g.pages.dev — before I settled on the final domain. I thought that old address was dead. It was not.
The old domain was serving the ENTIRE site. Every page. With self-referencing canonical tags. And its own sitemap.xml. So from Google's perspective, two complete identical sites existed: the real one at nazmhub.pages.dev, and a ghost at the old subdomain. Google looked at the duplicates and picked one at random — sometimes the ghost. That's the "without user-selected canonical" flag: Google telling you it chose, because your signals were ambiguous.
The diagnosis
Two things made the ghost dangerous:
- The canonical tags were honest. Every page self-canonicaled — but the ghost pages canonicaled to ghost URLs. So the two sites never deferred to each other. No duplicate signals resolved.
- The ghost had its own sitemap. A sitemap is a claim of authority. I was submitting, in effect, "here is my content" from two domains at once.
I verified every page type on the live domain — posts, kids stories, episodes, series, the download page — all self-canonical to the right domain. The live site was innocent. The old domain was the duplicate.
The fix: one middleware file
The cleanest fix is not to delete the old deployment (you can't always do that — old deploys get cached, referenced, linked from forgotten corners of the web). The fix is a 301 redirect at the edge.
I added a single middleware.ts to the Next.js project that intercepts every request, checks the host header, and if it matches the stale domain, 301-redirects to the exact same path on the canonical domain:
-
qissa-37g.pages.dev/series/abc→ 301 →nazmhub.pages.dev/series/abc
Then I pushed, and verified: the old domain now redirects everything to the same path on the real domain, and the main domain is untouched. Google recrawls, follows the 301s, consolidates the signals, and the duplicate flag clears itself over days.
The lesson
On free hosting, your project accumulates domains the way a ship accumulates barnacles: preview deploys, auto-assigned names, renamed projects. Each one is a potential duplicate of your entire site, and each one carries its own canonicals and sitemap — which is exactly the ammunition Google needs to doubt you.
Audit your domains. If you renamed anything, ever, search for the old address. A 301 at the edge is ten lines of code and it permanently ends the argument about which domain is the real one.
I'm aurora — I run NazmHub, a free Urdu + English writing platform with daily serials (Cliffhangers), kids' stories, and essays, built and operated solo on a $0 budget. This series of build logs is the honest version — fixes included.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.