Every static-site generator has a single line in its config that sets the site’s canonical URL. In Astro it is site: in astro.config.mjs. In Next.js it is NEXT_PUBLIC_SITE_URL. In Hugo it is baseURL. That one line propagates into every generated <link rel="canonical">, every <meta property="og:url">, every entry in your sitemap.xml, every JSON-LD @id, and every relative internal link that gets rewritten to absolute. When it is wrong, every one of those bleeds SEO in silence — because the pages still render, the site still deploys, and there is no build error to catch. This post is the incident write-up for exactly that failure mode on this site (cosmoswebtech.com.au) — how a one-letter typo propagated to 108 files, how the audit surfaced it, and the pre-launch checklist that would have prevented it.

What “Canonical Domain Drift” Actually Means
The literal typo was cosmowebtech.com.au (missing the s in cosmos) instead of the correct cosmoswebtech.com.au. The wrong domain was configured in the Astro site’s site: field at some point during setup, and everything downstream inherited it. Concretely:
sitemap.xmllisted every URL under the wrong host, so Google Search Console kept trying to crawl a domain we do not own.<link rel="canonical">on every rendered page pointed at the wrong domain, telling Google “the real version of this page lives over there.”<meta property="og:url">in the head of every page told Facebook and LinkedIn the same wrong URL, so preview cards linked to a 404 if the crawler followed them.- JSON-LD (
schema.orgmarkup for Article, Organization) had@idandurlfields pointing at the wrong domain, weakening every schema signal. - Internal links written as relative paths were being rewritten to absolute against the wrong domain during build, so every “read more” link on the site pointed users at a domain that did not resolve.
The site itself was live and working at the correct domain — Cloudflare Pages was serving cosmoswebtech.com.au fine, and users clicking through from Google would land on real pages. But every crawlable, indexable, shareable metadata field was pointing at the wrong domain. Google, Facebook, LinkedIn, and every LLM crawler that respects canonical URLs were being told the site lived somewhere else.
Why This Failure Is Silent

The scary part of a canonical-domain typo is that nothing complains. Astro built cleanly. The site deployed cleanly. Cloudflare returned 200s on every page. Lighthouse gave good scores. Web Vitals were green. Nothing in the normal deploy pipeline treats “the domain we told the site it lives at” as something worth validating.
The user impact is invisible for weeks because:
- Google Search Console treats a canonical mismatch as a suggestion, not an error. Pages still get indexed, just at the canonical URL — which is the wrong domain — so they never rank at the actual domain.
- Facebook / LinkedIn preview cards silently link to the wrong URL. If someone shares your post and clicks the preview, they land on a 404 hosted by whoever registered the typo domain (or on nothing).
- Direct traffic to the correct domain works fine, so your Analytics numbers look normal.
- Google Search visibility slowly drops over weeks as the wrong canonicals accumulate and the real URLs get de-prioritised. By the time you notice, you have already lost the ground.
The only signal we had that something was wrong was Search Console reporting “0 discovered URLs” for the site — which we initially misdiagnosed as a sitemap configuration issue.
Finding the Blast Radius

Once we identified the root cause, the audit was straightforward. Every file with the wrong domain needed to be found. grep -r across the repo revealed 108 files touching the wrong string in one form or another:
grep -r "cosmowebtech" src/ public/ astro.config.mjs \
--include="*.astro" \
--include="*.mdx" \
--include="*.md" \
--include="*.json" \
--include="*.mjs" \
--include="*.ts" \
| wc -l
The 108 hits broke down roughly as:
- 1 file — the
site:field inastro.config.mjs(the root cause). - ~85 files — MDX blog posts that had absolute URLs embedded in frontmatter (
image:,ogImage:,heroImage:) or in body content (inline links to sister posts). These were written against whateversite:was configured at the time. - ~15 files —
.astrolayouts and components with hardcoded absolute URLs (schema.org markup, canonical link tags, structured-data@idfields). - ~5 files — JSON files (
sitemap-index.json, robots-adjacent configs,llms.txtreferences). - ~2 files — Small scripts and utilities that generated absolute URLs from the site domain.
The fix was one large search-and-replace across the codebase, followed by:
# Verify zero occurrences remain
grep -r "cosmowebtech" . --exclude-dir=node_modules --exclude-dir=.git
# (no output means all fixed)
Then rebuild, re-deploy, and submit a fresh sitemap.xml to Search Console.
The Pre-Launch Checklist That Would Have Prevented It

Every canonical-domain drift incident I have seen (this is not the first) traces back to the same class of mistake: the site: value was set once during initial setup, before the actual domain was finalised, and never re-verified. The fix is a two-item pre-launch checklist:
1. Verify the site: config resolves to a domain you actually own.
Before the first production deploy, run:
# For Astro
grep "^ site:" astro.config.mjs
# Then check the returned host actually resolves and returns 200
curl -sI https://cosmoswebtech.com.au/ | head -1
# Expected: HTTP/2 200
If the config value returns anything other than a 200 from a domain your organisation owns, stop. Do not deploy.
2. Cross-check that generated metadata matches the config.
After the build, spot-check three surfaces:
- The canonical tag on the homepage:
curl -s https://your-domain/ | grep 'rel="canonical"' - The OG URL on a random blog post:
curl -s https://your-domain/blog/post-slug/ | grep 'og:url' - The first URL in the sitemap:
curl -s https://your-domain/sitemap.xml | grep -m1 '<loc>'
All three should return the actual production domain. If any of them does not, you have canonical drift.
What to Run Right Now on Your Own Site

If you run any static site (Astro, Next.js, Hugo, 11ty, Gatsby, Nuxt, Sveltekit) and have not deliberately verified this since the site launched, run these three commands. They take under a minute and will surface the exact class of bug this post is about:
# 1. What does your build config say the site is?
grep -E "^(site|siteUrl|baseUrl|NEXT_PUBLIC_SITE_URL)" astro.config.mjs next.config.js hugo.toml 2>/dev/null
# 2. What does the deployed site's canonical actually say?
curl -sI https://your-real-domain.com/ | head -1
curl -s https://your-real-domain.com/ | grep -oE 'rel="canonical" href="[^"]+"'
# 3. What does the deployed sitemap actually say?
curl -s https://your-real-domain.com/sitemap.xml 2>/dev/null | grep -oE '<loc>[^<]+' | head -3
If the domain in output 1 does not match the domain in output 2 and 3, you have the same problem we did.
Post-Incident: How We Re-Established Search Visibility

Fixing the domain in the code was the easy part. Rebuilding the SEO position took longer. The recovery steps that mattered:
- Push the fix and confirm re-deploy. Deploy the corrected site. Curl a few pages to verify canonicals now show the right domain.
- Re-submit the sitemap in Google Search Console. Under Sitemaps, remove the old (broken) sitemap URL if it was submitted incorrectly, then add the correct one.
- Request re-indexing of key pages. Use the URL Inspection tool for your top 10-20 pages and click “Request Indexing” on each. Google will re-crawl within days.
- Wait. Google’s index takes 1-4 weeks to re-establish the corrected canonicals as the source of truth. There is no shortcut.
- Monitor “Pages” in Search Console. Watch for the “Duplicate, Google chose different canonical” errors that would indicate the wrong canonical is still winning somewhere. Zero of those means the fix is fully absorbed.
On our site, Search Console started reporting real “Discovered URLs” within 5 days of the fix and re-submission. Real ranking recovery took about three weeks. Cost of the incident: an estimated 4-6 weeks of lost Search visibility while the wrong canonicals accumulated, plus about 3-4 hours of fix and audit time. Preventable with 30 seconds of pre-launch cross-check.
The Cheapest SEO Insurance You Can Buy
Add this to your CI pipeline and never think about it again:
# .github/workflows/deploy.yml or your CI config
- name: Verify canonical domain matches expected
run: |
EXPECTED="cosmoswebtech.com.au"
ACTUAL=$(grep "^ site:" astro.config.mjs | grep -oE 'https://[^"]+' | sed 's|https://||;s|/$||')
if [ "$EXPECTED" != "$ACTUAL" ]; then
echo "ERROR: site config ($ACTUAL) does not match expected domain ($EXPECTED)"
exit 1
fi
Ten lines of YAML. Prevents an entire class of silent SEO regression forever. Every static-site build should have some version of this.
If you run a Sydney small business website and your Google Search visibility has drifted downward inexplicably over the past few months, this is the specific thing worth checking first. The typo does not have to be visible to a human — a missing letter in a config file three months ago is enough to explain a slow bleed of ranking that no other diagnostic will surface.