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.
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):
- Google Search Central — Consolidate duplicate URLs (canonicalization): that identical or very similar content at multiple URLs makes a search engine choose a canonical, and that a
rel="canonical"link is a signal Google may honour, not a directive it must obey. - Google Search Central — Block Search indexing with noindex: that a
noindexrule is how you ask Google to drop a page from the index, and that the page must remain crawlable for the rule to be seen. - Google Search Central — Redirects and Google Search: that permanent redirects are the recommended way to move a URL and consolidate its signals onto the destination.
Keep reading: all posts on the WizusLabs Engineering blog.