46% of websites have canonical tag issues. An invalid canonical tells search engines to index the wrong URL - or worse, no URL at all - fragmenting your page authority across duplicates.
Canonical tags tell search engines which version of a page is the "official" one when multiple URLs serve similar or identical content. When the canonical URL is invalid, search engines can't follow your instructions.
This creates several problems:
Duplicate content dilution. Without a valid canonical, Google might index /products/widget, /products/widget?ref=email, and /products/widget?utm_source=twitter as separate pages. Your page authority splits three ways instead of consolidating.
Crawl budget waste. Search engines spend time crawling URLs that should point to a single canonical, leaving less budget for your actual unique content.
Ranking instability. Google picks whichever URL it prefers, which might change week to week. Your rankings fluctuate as the indexed URL shifts.
The technical causes vary. Lighthouse fails this audit when your canonical tag:
Each of these confuses search engines about which URL to index.
Check your canonical in the Elements panel:
Ctrl/Cmd + F in Elements panelrel="canonical" or rel=canonicalhref valueCommon problems to look for:
<!-- Invalid URL -->
<link rel="canonical" href="not-a-valid-url">
<!-- Relative URL (should be absolute) -->
<link rel="canonical" href="/products/widget">
<!-- Points to homepage when it shouldn't -->
<link rel="canonical" href="https://example.com/">
<!-- Multiple conflicting canonicals -->
<link rel="canonical" href="https://example.com/page-a">
<link rel="canonical" href="https://example.com/page-b">
Quick console check:
const canonicals = document.querySelectorAll('link[rel="canonical"]')
canonicals.forEach(link => console.log(link.href))
// Should output exactly one absolute URL matching the current page
Run a Lighthouse SEO audit. Look for "Document does not have a valid rel=canonical" in the results.
The explanation will indicate the specific problem:
Every page should have exactly one canonical pointing to itself (or to the preferred version if duplicates exist):
<head>
<link rel="canonical" href="https://example.com/products/widget">
</head>
Requirements:
https://)<head>, not <body>For self-referencing canonicals (the most common case):
<!-- On https://example.com/blog/my-post -->
<link rel="canonical" href="https://example.com/blog/my-post">
When the same content exists at multiple URLs, canonicalize to one:
<!-- All these pages should have the same canonical -->
<!-- https://example.com/products/widget -->
<!-- https://example.com/products/widget?ref=email -->
<!-- https://example.com/products/widget?utm_source=twitter -->
<!-- https://www.example.com/products/widget -->
<link rel="canonical" href="https://example.com/products/widget">
Choose your canonical URL wisely:
Relative URLs:
<!-- Wrong -->
<link rel="canonical" href="/products/widget">
<!-- Right -->
<link rel="canonical" href="https://example.com/products/widget">
Homepage canonical on all pages:
<!-- Wrong - every page points to homepage -->
<link rel="canonical" href="https://example.com/">
<!-- Right - each page points to itself -->
<link rel="canonical" href="https://example.com/about">
Multiple conflicting canonicals:
<!-- Wrong - two different canonicals -->
<link rel="canonical" href="https://example.com/page-a">
<link rel="canonical" href="https://example.com/page-b">
<!-- Right - single canonical -->
<link rel="canonical" href="https://example.com/page-a">
HTTP canonical header conflict:
Check if your server sends an X-Canonical or Link header that conflicts with your HTML canonical:
curl -I https://example.com/page | grep -i canonical
If both exist with different values, they conflict. Choose one method and be consistent.
For dynamic sites, generate canonicals programmatically:
// Build canonical from current URL, stripping query params
function getCanonicalUrl() {
const url = new URL(window.location.href)
url.search = '' // Remove query parameters
url.hash = '' // Remove hash
return url.href
}
// PHP example
$canonical = 'https://' . $_SERVER['HTTP_HOST'] . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
echo '<link rel="canonical" href="' . htmlspecialchars($canonical) . '">';
export const metadata = {
alternates: {
canonical: 'https://example.com/page'
}
}
// Or dynamically
export async function generateMetadata({ params }) {
return {
alternates: {
canonical: `https://example.com/products/${params.slug}`
}
}
}
useSeoMeta or useHead:<script setup>
const route = useRoute()
const config = useRuntimeConfig()
useHead({
link: [
{ rel: 'canonical', href: `${config.public.siteUrl}${route.path}` }
]
})
</script>
After implementing canonical tags:
1. View page source
Check that exactly one <link rel="canonical"> appears in <head> with a valid absolute URL.
2. Re-run Lighthouse
The "Document does not have a valid rel=canonical" audit should pass.
3. Test URL variations
Visit different versions of your URLs (?ref=test, ?utm_source=email) and confirm they all have the same canonical pointing to the clean URL.
4. Check Google Search Console
Use URL Inspection to see which canonical Google detected. If it differs from yours, Google is overriding your choice - usually because something else signals a different preferred URL.
5. Validate with curl
curl -s https://example.com/page | grep -i canonical
Confirm the output shows your expected canonical URL.
<body> are ignored by search engines. Must be in <head>.Canonical issues often appear alongside:
A single template bug can add invalid canonicals to thousands of pages. A CMS plugin might inject conflicting canonicals. Pagination might point all pages to page 1. Unlighthouse scans your entire site and flags every page with canonical issues, helping you catch systemic problems before they tank your rankings.