Canonical Setup
Configuring canonical tags to tell search engines which URL is the authoritative version — preventing duplicate-content penalties when the same page is reachable via multiple URLs.
Why
When the same content is reachable via multiple URLs (http vs https, with/without trailing slash, with/without query params, www vs non-www), Google sees duplicate content and either picks one arbitrarily (which may be the wrong one) or deindexes all of them. Canonical tags solve this by telling Google 'this URL is the authoritative version, index this one and ignore the others'. Without canonicals, a site with /launch and /launch/ (trailing slash) and /launch?ref=ph splits its ranking signal 3 ways and ranks lower than a site with a single canonical URL. Canonical setup is especially critical at launch because new pages often have multiple entry points (the blog links to /launch?ref=blog, Product Hunt links to /launch?ref=ph) and without canonicals, each variant competes against the others in the index.
When
During the build, as part of the BaseLayout or per-page head. Verify with a crawl on staging before launch and on production within 24 hours of deploy.
How
1. Define the canonical URL pattern: https://ez4code.com/launch (https, no trailing slash, no query params, no www) — this is the one true URL for every page
2. In the BaseLayout, add <link rel="canonical" href={canonical}> where canonical is built from the page path (not from window.location, which includes query params)
3. For each page, the canonical must be self-referencing: /launch canonical = https://ez4code.com/launch, /launch/[slug] canonical = https://ez4code.com/launch/[slug]
4. For paginated pages: /launch?page=2 canonical should point to /launch (the first page), not to /launch?page=2 — this consolidates ranking signal to the first page
5. For i18n pages: /zh/launch canonical = https://ez4code.com/zh/launch (each language version is its own canonical, connected via hreflang, not canonical)
6. For filtered/sorted views: /launch?subdomain=producthunt canonical = https://ez4code.com/launch (the unfiltered view is canonical; filtered views are not indexed separately)
7. Set up 301 redirects for non-canonical URL forms: http:// → https://, ez4code.com/launch/ → ez4code.com/launch (trailing slash), www.ez4code.com → ez4code.com
8. Verify with a crawl: Screaming Frog will flag any page whose canonical does not match its URL (after redirect normalization); fix all flags before launch
9. In Google Search Console, check the 'Duplicate without user-selected canonical' report post-launch — any pages here are canonical issues you missedExample
# Canonical Setup — ez4code v2.0
## BaseLayout.astro (canonical logic)
---
const canonical = `${SITE_URL}${prefix}${path}`;
---
<link rel="canonical" href={canonical}>
## Per-page canonical
/launch → https://ez4code.com/launch
/launch/pre-launch-checklist → https://ez4code.com/launch/pre-launch-checklist
/zh/launch → https://ez4code.com/zh/launch
/zh/launch/ph-preparation → https://ez4code.com/zh/launch/ph-preparation
## Non-canonical forms (301 redirect to canonical)
http://ez4code.com/launch → 301 → https://ez4code.com/launch
ez4code.com/launch/ → 301 → https://ez4code.com/launch
www.ez4code.com/launch → 301 → https://ez4code.com/launch
ez4code.com/launch?ref=ph → canonical → https://ez4code.com/launch
## Paginated
/launch?page=2 → canonical → https://ez4code.com/launch
/launch?page=3 → canonical → https://ez4code.com/launch
## Filtered
/launch?subdomain=producthunt → canonical → https://ez4code.com/launch
## Verification (Screaming Frog)
77 URLs crawled
0 "canonical mismatch" flags
0 "duplicate without canonical" flags
=> Canonical setup correct
## Post-launch (Search Console)
"Duplicate without user-selected canonical" report: 0 URLs ✓Common Problems
- ⚠Building canonical from window.location instead of the page path — window.location includes query params (?ref=ph), so every referral variant gets a different canonical and they all compete; build canonical from the route path only.
- ⚠Trailing-slash inconsistency — /launch and /launch/ are different URLs to Google; without a 301 redirect from one to the other, both get indexed and split ranking signal; pick one form (no trailing slash is conventional) and redirect the other.
- ⚠Canonical to a 404 or a redirect — if the canonical URL itself returns 404 or 301, Google ignores the canonical and indexes whatever it finds; verify every canonical URL returns 200.
- ⚠i18n pages using canonical instead of hreflang — /zh/launch canonical pointing to /launch tells Google to deindex the Chinese version; use hreflang for language alternates and self-referencing canonical for each language.
Best Practices
- ✓Build canonical from the route path + SITE_URL constant, never from window.location — this ensures query params and fragments never enter the canonical, keeping it stable across referral variants.
- ✓Enforce a single URL form via 301 redirects at the edge (Cloudflare Pages _redirects or vercel.json) — http→https, trailing-slash, www→non-www should all be edge redirects, not per-page logic.
- ✓Use self-referencing canonical for every page, including i18n variants — /zh/launch canonical is https://ez4code.com/zh/launch, not https://ez4code.com/launch; hreflang connects the variants, canonical defines each one's authority.
- ✓Run Screaming Frog's 'Canonical Mismatch' report after every deploy — this catches canonical regressions (a new page with a wrong canonical) within hours, not months.