WizusLabs Engineering · Craft

One canonical home: the day we deleted 800 pages of our own site

Here is a sentence that should not be true: we made our website easier to find by deleting most of it. On 2026-07-10 the WizusLabs site went from two live copies to one — and the copy we stripped had roughly eight hundred pages in it.

By WizusLabs Engineering · 2026-07-22 · ~7 min read

For about a month, wizuslabs.com and wizuslabs.github.io served the same site from two different addresses. To a person that reads as a sensible fallback: if one host hiccups, the other is right there. To a search engine it reads as something worse than redundant — it reads as a site competing with itself. Two URLs, one set of content, and the ranking signal that should have pointed at a single home split cleanly down the middle. The fix was not to tune anything. It was to decide, once, which address is the real one, redirect the other to it path-for-path, and let the pages we removed drop out of the index on their own. We took the losing origin from 803 files down to 7 — and the studio is more findable for it, not less.

Two live copies is one site too many

The duplication was an accident of history, which is the only way this kind of thing ever happens. The site started life deploying to GitHub Pages at wizuslabs.github.io. On 2026-06-10 we cut over to Cloudflare Pages at wizuslabs.com, and for a stretch afterward both origins were still live, still crawlable, still serving the full site. Nobody chose to run two copies; we just never turned the first one off. Google, doing exactly its job, indexed the duplicate github.io origin alongside the real one.

The insidious part is that neither copy was wrong. A duplicate-content problem is not a broken page or a bad link — every URL resolved, every page rendered, nothing 404’d. It is a problem of ambiguity. When the identical privacy policy lives at two addresses, a crawler has to guess which one is authoritative, and any authority the page has earned — links, history, trust — is divided between the two instead of pooled on one. You do not get penalised so much as diluted. The site was quietly halving its own case in a contest it did not know it had entered, against the only opponent that could reliably beat it: itself.

Pick one home — and say so in the markup

The first move costs nothing and is easy to overstate. Every page on the site already carries a self-referential canonical link that names its wizuslabs.com address as the authoritative one, in the extensionless form the host serves with a 200; the .html variants 308-redirect to those clean URLs. That is the honest way to write a link and the subject of an earlier devlog, write the canonical form — you state the one true address everywhere rather than leaving a reader, human or machine, to normalise it for you.

But a canonical tag is a hint, not a command. It tells a crawler which URL you would prefer it treat as the original; it does not stop the other origin from existing, being linked, or being indexed on its own merits. As long as github.io kept answering with a full, healthy copy of the site, we were asking the search engine to please ignore a door we had propped wide open. The declaration and the reality have to agree. Which meant the real work was not in the markup at all — it was in taking the second origin down to something that could not be mistaken for a second home.

One site served from two origins, before and after consolidation Before: wizuslabs.com and wizuslabs.github.io both serve the full site of roughly 800 pages, and a search index ingests both, so the ranking signal for one site is split across two addresses. After: wizuslabs.com is the sole content origin the search index ingests, while the github.io origin is reduced to a 7-file redirect shell that 308-redirects to wizuslabs.com and is not indexed as content — one canonical home, one consolidated signal. Before one site, two live origins wizuslabs.com ~800 pages github.io ~800 pages (copy) search index signal split across two URLs After one canonical home wizuslabs.com sole content origin github.io 7 files · redirect shell search index 308 redirect
The fix is not a clever tag — it is removing the ambiguity. One origin serves the site; the other is stripped to a signpost that points at it and is no longer indexed as content.

Strip the loser to a signpost

So we gutted github.io. The repository went from 803 files to 7, and the arithmetic of what survived is the whole design. We replaced the old index.html and 404.html with small, self-contained pages whose only job is to redirect, path-for-path, to the matching address on wizuslabs.com. A visitor — or an old store listing — that still reaches github.io/neuralspark/privacy-policy.html lands on the live wizuslabs.com equivalent instead of a dead end. We removed everything else: the duplicate pages, and, critically, the github.io sitemap.xml, because a sitemap is an active invitation to index and the last thing we wanted was to keep inviting crawlers to the copy we were retiring. A path that no longer exists now returns a 404 that bounces to a noindex page — the polite way to tell a search engine “this is gone, drop it” rather than leaving a stale entry to rot in the index.

Three files we kept on purpose, and none of them is content. We left version.json in place because aged app binaries still in the field fetch their update manifest from that exact URL, and it stays byte-identical so those old installs keep working — the same never-break-a-shipped-URL discipline we wrote about in the hidden cost of renaming a shipped app. We kept app-ads.txt, the ad-network authorisation file that some crawlers expect at the legacy host. And we kept the Google Search Console verification token, so we can still watch the old origin de-index cleanly rather than flying blind. Everything that stayed earns its place by serving a machine that cannot be updated; everything a human reads now lives at one address.

The trade-off we took on purpose

This is not a free win, and pretending it is would be the dishonest version of this post. We gave up a real fallback host. When two origins served the full site, an outage on one was survivable; now wizuslabs.com is the sole content origin, and if Cloudflare Pages has a bad day there is no second copy quietly holding the fort. We decided a clean, consolidated identity is worth more than a redundancy we were paying for in ranking signal — but it is a decision, with a cost, not a strict upgrade.

And the redirect shell is now a thing we have to remember exists. A stripped-down origin is only stable if nobody repopulates it, and the most dangerous hand on that lever is our own. Our deploy checkout is intentionally divergent from the stripped remote; if anyone ever pushed the full site back to github.io out of muscle memory, they would resurrect every deleted duplicate and re-break the exact problem we just fixed — silently, because the deploy would look successful. So the guard is a physical speed bump, not a note in a wiki: the deploy script refuses to push to github.io unless an explicit WIZUSLABS_ALLOW_GITHUB_IO_PUSH=1 is set. The destructive action is possible, deliberately, but it can never be the default or the accident. A one-way door with a clearly labelled override is safer than a door you have merely promised not to walk through.

You cannot see the payoff the way you can see a fixed crash. Search rankings move slowly, they move for a hundred reasons at once, and we did not run — could not run — a controlled experiment that would let us hang a number on this. What we can say is mechanical and true: a site that answered to two addresses now answers to one, the duplicate origin is de-indexing itself on schedule, and no page a person cares about got harder to reach in the process. The rigour here is in the mechanism, not in a graph we could have dressed up.

A website is not really its pages; it is its addresses. Two live copies of the same content is not twice the site — it is half of one, arguing with the other half in front of the one audience you were trying to reach. The cure was subtraction: pick the home, point everything else at it, and delete the rest without sentiment. If you want the longer version of how the small decisions behind a shipped product compound, it is on the WizusLabs Engineering blog. A site should answer to exactly one name.

Notes

This is a first-hand engineering account of a change we made to our own site on 2026-07-10, not a general SEO playbook or a benchmark. It deliberately carries no traffic, ranking, or “visibility” metrics — we did not run a controlled study, search rankings shift for many reasons at once, and inventing a before/after number would undercut the whole point of the piece. The hard facts — the 2026-06-10 cutover to Cloudflare Pages, the 2026-07-10 github.io sunset, the reduction from 803 files to 7, the exact files kept (version.json, app-ads.txt, and the Search Console token), the path-preserving redirects, and the WIZUSLABS_ALLOW_GITHUB_IO_PUSH=1 deploy guard — trace to our own project change log and are stated as they happened. The general mechanics we lean on (that duplicate URLs split a site’s signal, that a canonical link is a hint rather than a command, and how noindex and redirects tell a crawler to consolidate or drop a URL) are documented by Google and cited below; search-engine behaviour changes over time, so verify specifics against those sources before relying on them.

Sources

The search-engine mechanics this post leans on are documented by Google Search Central (living documents; accessed 2026-07-22):

Keep reading: all posts on the WizusLabs Engineering blog.

← Back to the Blog