# Missing canonical link

> Duplicate URLs (trailing slash, query params) split your ranking. Add <link rel="canonical"> to point search engines at one address.

_Category: Search visibility · Detector `seo` · Check `canonical-missing` · Severity: info_

The same content is often reachable at several URLs — with and without a trailing slash, with query or tracking params, at www and apex. Without `<link rel="canonical">`, search engines may treat these as separate pages and split ranking signals between them. AI-built apps generate lots of parameterised and duplicated routes, making this common.

## Symptoms

- The same page indexed under multiple URLs
- Ranking signals split between `?utm` and clean URLs
- www vs non-www or trailing-slash duplicates in Search Console

## How VibeCheck detects it

The `seo` detector flags this live in the browser and reports it to the widget's Problems list — and to your coding agent over MCP.

- **Issue string:** `Missing canonical link`
- **Threshold:** no <link rel="canonical">

## Root causes

- Routes are reachable at multiple URL variants with no canonical declared
- Query and tracking parameters create infinite URL variants
- The head is unmanaged so no canonical tag is emitted

## The fix

Add a `<link rel="canonical">` whose `href` is the page’s single preferred absolute URL — clean, no tracking params, with a consistent trailing-slash and host convention. Self-reference it on the canonical page itself.

### Steps

1. Pick one canonical URL convention (host, trailing slash) and apply it everywhere
2. Emit `<link rel="canonical">` with the clean absolute URL on every page
3. Point duplicate/parameterised variants at the same canonical

```html
<link rel="canonical" href="https://acme.com/pricing" />
```

## Framework-specific fixes

### React

Render `<link rel="canonical">` with an absolute URL (React 19), or use `react-helmet-async` on React 18.

```tsx
<link rel="canonical" href="https://acme.com/pricing" />
```

### Next.js

Set `alternates.canonical` in metadata. A relative path resolves against `metadataBase`, which you set once in the root layout.

_app/layout.tsx sets metadataBase; pages set canonical_

```tsx
// app/layout.tsx
export const metadata: Metadata = { metadataBase: new URL('https://acme.com') }

// app/pricing/page.tsx
export const metadata: Metadata = { alternates: { canonical: '/pricing' } }
```

[Next.js docs](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#alternates)

### Vue

Add a link entry to `useHead`.

```ts
useHead({ link: [{ rel: 'canonical', href: 'https://acme.com/pricing' }] })
```

### Svelte

Emit the canonical link in `<svelte:head>`, building the URL from `$page.url`.

```svelte
<script>
  import { page } from '$app/stores'
</script>

<svelte:head>
  <link rel="canonical" href={`https://acme.com${$page.url.pathname}`} />
</svelte:head>
```

### Vanilla JS

Add the canonical link to each page’s `<head>` with the clean absolute URL.

```html
<link rel="canonical" href="https://acme.com/pricing" />
```

## FAQ

### Should a page canonicalise to itself?

Yes. The preferred version should carry a self-referencing canonical. Duplicate variants then point at that same URL.

### Does canonical fix duplicate content penalties?

There is no “penalty” per se, but canonical consolidates ranking signals onto one URL so duplicates do not compete with each other.

### Absolute or relative canonical URL?

Use absolute URLs. They are unambiguous to crawlers. In Next.js a relative path is fine because `metadataBase` makes it absolute at build time.

## Related problems

- [Missing og:url](https://vibecheck.wcgw.fun/fix/missing-og-url.md) — Search visibility
- [Unfriendly URL slug](https://vibecheck.wcgw.fun/fix/unfriendly-url-slug.md) — Search visibility
- [Page is set to “noindex”](https://vibecheck.wcgw.fun/fix/accidental-noindex.md) — Search visibility
- [Missing page title](https://vibecheck.wcgw.fun/fix/missing-page-title.md) — Search visibility

---

Fix guide from VibeCheck — https://vibecheck.wcgw.fun/fix/missing-canonical-url. Full site index for LLMs: https://vibecheck.wcgw.fun/llms.txt
