---
title: "Canonical URLs in Nuxt"
description: "Add canonical URLs in Nuxt with useHead so duplicate pages don't split your rankings, and learn when Google overrides your declared canonical."
canonical_url: "https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/canonical-urls"
last_updated: "2026-07-16"
---

<key-takeaways>

- Canonical tags are hints Google usually honors, not directives it's required to follow
- Always use absolute URLs (`https://mysite.com/page`, not `/page`)
- Self-referencing canonicals are recommended even for pages with no duplicates
- Don't combine a canonical with noindex; the two send conflicting signals

</key-takeaways>

Canonical URLs tell search engines which version of a page is the primary copy when duplicate content exists at multiple URLs. Use them for URLs with query parameters (filters, sorting), the same content on multiple paths, paginated sequences, and cross-domain syndication.

For choosing between canonicals and redirects, see [Duplicate Content](/learn-seo/nuxt/controlling-crawlers/duplicate-content). For redirecting users, use [HTTP redirects](/learn-seo/nuxt/controlling-crawlers/redirects) instead. For blocking pages from search, use [meta robots](/learn-seo/nuxt/controlling-crawlers/meta-tags) with noindex.

## Quick Setup

Add canonical URLs to your Nuxt pages using `useHead()`:

```ts [Basic Usage]
useHead({
  link: [
    {
      rel: 'canonical',
      href: 'https://mysite.com/products/phone'
    }
  ]
})
```

```ts [With Query Params]
// Keep sort parameter in canonical
useHead({
  link: [
    {
      rel: 'canonical',
      href: `https://mysite.com/products?sort=${sort}`
    }
  ]
})
```

```ts [Cross Domain]
useHead({
  link: [
    {
      rel: 'canonical',
      href: 'https://otherdomain.com/original-article'
    }
  ]
})
```

<module-card className="w-1/2" slug="seo-utils">



</module-card>

## Understanding Canonical URLs

A canonical URL is implemented as a link tag in your page's head:

```html
<link rel="canonical" href="https://mysite.com/page">
```

### Canonical Tags Are Hints, Not Directives

Google treats canonicals as [strong signals, not mandatory rules](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls): Google does not require any canonicalization method, and a site can rank fine without specifying a preference at all. Google weighs your signal alongside other factors when it decides which URL to show in results.

Google may choose a different canonical than you specify when:

- Content differs significantly between URLs
- Multiple conflicting canonical declarations exist
- Google believes a different page is more authoritative

[Google uses the canonical page](https://developers.google.com/search/docs/crawling-indexing/canonicalization) as the main source to evaluate content and quality. Non-canonical URLs may still be crawled but usually won't appear in search results.

<warning>

Canonicals only work when the target page has substantially similar content to the source. Pointing a canonical at a different page or your homepage doesn't consolidate rankings; Google is likely to ignore it.

</warning>

### Important Notes

[Self-referencing canonicals are best practice](https://searchengineland.com/canonicalization-seo-448161) even for pages with a single, unique source of content: they establish a clear preferred URL and prevent search engines from guessing when tracking parameters or alternate URL formats appear.

- Must use absolute URLs ([Google documentation](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls))
- Only one canonical per page (multiple declarations cause Google to ignore all hints)
- Best served in the initial HTML: AI crawlers like GPTBot and ClaudeBot [never execute JavaScript](https://vercel.com/blog/the-rise-of-the-ai-crawler), so a canonical injected client-side is invisible to them
- Include canonical pages in [sitemaps](/learn-seo/nuxt/controlling-crawlers/sitemaps), exclude non-canonical pages
- Don't combine a canonical with [noindex meta robots](/learn-seo/nuxt/controlling-crawlers/meta-tags): they send conflicting signals

## Common Patterns

### Filter and Sort Parameters

```ts [pages/products/[category].vue]
const route = useRoute()
const { sort, filter, page } = route.query
const category = route.params.category

useHead({
  link: [{
    rel: 'canonical',
    // Only include sort in canonical, remove filter and pagination
    href: sort
      ? `https://mysite.com/products/${category}?sort=${sort}`
      : `https://mysite.com/products/${category}`
  }]
})
```

Paginated pages need their own self-referencing canonicals too. [Don't point them all to page 1](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading): each page in the sequence has unique content and should be indexed separately. See the [Pagination SEO guide](/learn-seo/nuxt/routes-and-rendering/pagination) for implementation details.

### Mobile/Desktop Versions

If you serve separate mobile URLs (`m.domain.com`), [the desktop URL is always the canonical](https://developers.google.com/search/docs/crawling-indexing/mobile/mobile-sites-mobile-first-indexing): point `rel=canonical` on the mobile page to the desktop URL, and `rel=alternate` on the desktop page to the mobile URL.

```ts [pages/products/[id].vue]
const route = useRoute()
const id = route.params.id

useHead({
  link: [{
    rel: 'canonical',
    // Mobile site (m.mysite.com) points to desktop
    href: `https://mysite.com/products/${id}`
  }]
})
```

Responsive design with a single URL avoids needing this canonical/alternate pairing at all, and is what Google recommends for new sites.

### Cross-Domain Syndication

[Google no longer recommends cross-domain canonicals](https://searchengineland.com/google-no-longer-recommends-canonical-tags-for-syndicated-content-406491) for syndicated content; a canonical tag doesn't reliably stop a syndicated copy from outranking the original. Have syndication partners block indexing with noindex meta robots instead.

```ts [pages/article/[slug].vue]
// If syndicating TO other sites, have them use noindex
useSeoMeta({
  robots: 'noindex, follow'
})
```

```ts [pages/original-article.vue]
// If this IS the original, use self-referencing canonical
useHead({
  link: [{
    rel: 'canonical',
    href: 'https://mysite.com/articles/original-slug'
  }]
})
```

## Testing

Use [Google Search Console's URL Inspection tool](https://support.google.com/webmasters/answer/9012289) to compare your declared canonical with Google's selected canonical. For a full detection workflow, see [Duplicate Content](/learn-seo/nuxt/controlling-crawlers/duplicate-content#finding-duplicate-content).

- Validate absolute URL format
- Check for canonical chains (A → B → C)
- Verify SSR implementation (view page source, not inspected HTML)
- Test with and without parameters
- Don't combine canonical with noindex [meta robots](/learn-seo/nuxt/controlling-crawlers/meta-tags)

## Handling Edge Cases

### Multiple Language Versions

For multilingual sites, combine canonicals with hreflang:

```ts
useHead({
  link: [
    {
      rel: 'canonical',
      href: 'https://mysite.com/en/page'
    },
    {
      rel: 'alternate',
      hreflang: 'fr',
      href: 'https://mysite.com/fr/page'
    }
  ]
})
```

### Protocol/WWW Variations

Handle through [server redirects](/learn-seo/nuxt/controlling-crawlers/redirects) in `nuxt.config.ts`:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  routeRules: {
    // Redirect /old-page to /new-page
    '/old-page': { redirect: { to: '/new-page', statusCode: 301 } }
  }
})
```

Or handle with Nitro middleware:

```ts [server/middleware/canonicalize.ts]
export default defineEventHandler((event) => {
  const host = getRequestHost(event)
  if (!host.startsWith('www.')) {
    return sendRedirect(event, `https://www.${host}${event.path}`, 301)
  }
})
```

### Dynamic Canonicals

For dynamic routes, create a composable for consistent canonical URLs:

```ts [composables/useCanonical.ts]
export function useCanonical(path: string) {
  const config = useRuntimeConfig()
  return {
    link: [{
      rel: 'canonical',
      href: `${config.public.siteUrl}${path}`
    }]
  }
}
```

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      siteUrl: process.env.NUXT_PUBLIC_SITE_URL || 'https://mysite.com'
    }
  }
})
```

## Checklist

<checklist id="nuxt-canonical-urls">

- Every page has a self-referencing canonical, even pages with no duplicates
- Canonical URLs are absolute (`https://...`), never relative
- Only one canonical tag exists per page
- Canonicals ship in the server-rendered HTML, not injected client-side
- Paginated pages each get their own canonical, not all pointing to page 1
- Canonical and noindex are never combined on the same page

</checklist>
