Canonical URLs tell search engines which version of a page is the primary copy when duplicate content exists at multiple URLs. 67.6% of websites have duplicate content issues due to poor canonicalization.
Use canonicals for URLs with query parameters (filters, sorting), same content on multiple paths, paginated sequences, and cross-domain syndication. For redirecting users, use HTTP redirects instead. For blocking pages from search, use meta robots with noindex.
Add canonical URLs to your Vue pages using Unhead composables:
useHead({
link: [
{
rel: 'canonical',
href: 'https://mysite.com/products/phone'
}
]
})
// Keep sort parameter in canonical
useHead({
link: [
{
rel: 'canonical',
href: `https://mysite.com/products?sort=${sort}`
}
]
})
useHead({
link: [
{
rel: 'canonical',
href: 'https://otherdomain.com/original-article'
}
]
})
For Vue applications, you'll need to install Unhead manually.
A canonical URL is implemented as a link tag in your page's head:
<link rel="canonical" href="https://mysite.com/page">
Google treats canonicals as strong signals, not mandatory rules. Google's Joachim Kupke: "It's a hint that we honor strongly. We'll take your preference into account, in conjunction with other signals, when calculating the most relevant page to display in search results."
Google may choose a different canonical than you specify when:
Google uses the canonical page as the main source to evaluate content and quality. Non-canonical URLs may still be crawled but usually won't appear in search results.
Self-referencing canonicals are recommended even for unique pages. They establish a clear preferred URL and prevent search engines from guessing when tracking parameters or alternate URL formats appear.
Google's John Mueller: "I recommend using a self-referential canonical because it really makes it clear to us which page you want to have indexed, or what the URL should be when it is indexed."
const { sort, filter, page } = useRoute().query
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}`
}]
})
Use self-referencing canonicals on paginated pages—don't point them all to page 1. Each page in the sequence has unique content and should be indexed separately.
const route = useRoute()
const page = route.params.page || '1'
useHead({
link: [{
rel: 'canonical',
// Each page references itself
href: `https://mysite.com/blog${page === '1' ? '' : `?page=${page}`}`
}]
})
Google deprecated rel=prev/next in 2019 and now automatically recognizes pagination patterns.
If you have separate mobile URLs (m.domain.com), keep the desktop URL as canonical even with mobile-first indexing. Google uses canonicals to understand which pages belong together, then internally selects the mobile version for indexing.
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}`
}]
})
Don't switch canonicals from desktop to mobile URLs—Google advises against this. Use responsive design with a single URL instead.
Google no longer recommends cross-domain canonicals for syndicated content. Instead, syndication partners should use noindex meta robots to block indexing.
// If syndicating TO other sites, have them use noindex
useSeoMeta({
robots: 'noindex, follow'
})
// If this IS the original, use self-referencing canonical
useHead({
link: [{
rel: 'canonical',
href: 'https://mysite.com/articles/original-slug'
}]
})
Exception: News publishers syndicating to Google News should still use cross-domain canonicals per Google's news publisher guidance.
Use the URL Inspection tool to verify canonical implementation:
Note: Live tests won't show Google-selected canonical—you'll only see this for already indexed pages.
Common reasons Google ignores your canonical preference:
Fix canonicalization issues by checking internal links, sitemap inclusion, and removing conflicting signals.
For multilingual sites, combine canonicals with hreflang:
useHead({
link: [
{
rel: 'canonical',
href: 'https://mysite.com/en/page'
},
{
rel: 'alternate',
hreflang: 'fr',
href: 'https://mysite.com/fr/page'
}
]
})
Handle through server redirects rather than canonicals:
import express from 'express'
const app = express()
app.use((req, res, next) => {
const host = req.get('host')
if (!host.startsWith('www.')) {
return res.redirect(301, `https://www.${host}${req.path}`)
}
next()
})
// server.js for Vite SSR
import express from 'express'
const app = express()
app.use((req, res, next) => {
const host = req.get('host')
if (!host.startsWith('www.')) {
return res.redirect(301, `https://www.${host}${req.path}`)
}
next()
})
import { defineEventHandler, getRequestHost, sendRedirect } from 'h3'
export default defineEventHandler((event) => {
const host = getRequestHost(event)
if (!host.startsWith('www.')) {
return sendRedirect(event, `https://www.${host}${event.path}`, 301)
}
})
For dynamic routes, ensure canonical URLs are consistent:
export function useCanonical(path: string) {
const siteUrl = import.meta.env.VITE_SITE_URL
return {
link: [{
rel: 'canonical',
href: `${siteUrl}${path}`
}]
}
}
If you're using Nuxt, check out Nuxt SEO which handles much of this automatically.
Robot Meta Tag
Control page-level indexing with meta robots tags. Block search results pages, manage pagination, and prevent duplicate content in Vue apps.
HTTP Redirects
301 redirects preserve SEO value when content moves. Implement server-side redirects in Vue SSR to pass link equity and maintain rankings.