---
title: "Duplicate Content SEO in Vue"
description: "Duplicate URLs can divide ranking signals and consume crawling resources. Fix them in Vue with canonical tags, 301 redirects, and parameter handling."
canonical_url: "https://nuxtseo.com/learn-seo/vue/controlling-crawlers/duplicate-content"
last_updated: "2026-10-05"
---

> Index of every page on this site: [llms.txt](https://nuxtseo.com/llms.txt)

::key-takeaways
- Inspect final rendered content and URL variations. Identical SPA HTML shells alone do not prove duplicate content
- Duplicate URL variants can divide signals and consume crawling resources. Google selects a canonical, which may differ from your preference
- Use `@unhead/vue` for canonical tags. Configure HTTP redirects separately at your server or host
::

Multiple URLs can expose the same content in a Vue app. Check rendered pages, query parameters, host variants, and trailing slashes. Google can render JavaScript, so an identical initial HTML shell does not establish that the final pages are duplicates.

[Google selects a canonical for duplicate or very similar pages](https://developers.google.com/search/docs/crawling-indexing/canonicalization). Duplicate URLs are not automatically a penalty. Content practices can separately violate [Google's spam policies](https://developers.google.com/search/docs/essentials/spam-policies). Consistent canonical signals can help Google choose your preferred URL.

## Common Causes

### URL Variations

**www vs non-www**

`www.mysite.com` and `mysite.com` are [distinct hosts with separate crawl budgets](https://developers.google.com/crawling/docs/crawl-budget). Choose one, redirect the other.

**HTTP vs HTTPS**

`http://mysite.com` and `https://mysite.com` create duplicates. Always redirect HTTP to HTTPS.

**Trailing slashes**

`/products` and `/products/` are different URLs. [Pick one format site-wide](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/trailing-slashes).

Configure host and HTTPS redirects at your server or host. Use a fixed public origin rather than deriving destinations from untrusted Host or forwarded headers. See [HTTP Redirects](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/redirects).

### Query Parameters

[URL parameters can create many URL variants](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/query-parameters). Some serve duplicate content; others change the products or pagination shown. Assess the content before consolidating them.

```
/products
/products?color=red
/products?color=red&size=large
/products?color=red&size=large&sort=price
/products?color=red&size=large&sort=price&page=2
```

**Fix:** For substantially duplicate variants, set [canonical tags](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/canonical-urls#common-patterns-filter-and-sort-parameters) to the preferred equivalent page. Keep distinct, useful category content indexable. If your policy excludes low-value filters, use [noindex](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/meta-tags) instead.

Parameter order matters too: `?sort=price&filter=red` and `?filter=red&sort=price` are identical content, different URLs. Enforce consistent parameter order in canonical URLs; see the [Query Parameters guide](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/query-parameters) for the implementation.

### Tracking Parameters and Pagination

Analytics params (`utm_source`, `fbclid`, `gclid`) don't change content but create duplicate URLs, while pagination changes can expose distinct content that needs its own URL.

**Fix: strip tracking parameters.** Keep utm_source, fbclid, and gclid available to analytics. Exclude them from canonical URLs when they do not change content. See the [Query Parameters guide](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/query-parameters) for the implementation.

**Fix: self-reference paginated pages.** [Each paginated page has unique content](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/pagination). Use self-referencing canonicals; don't point page 2 to page 1. See the [Pagination SEO guide](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/pagination) for the implementation.

### Print and Mobile Versions

Printer-friendly URLs (`/article?print=true`) and mobile subdomains (`m.mysite.com`) create duplicates.

**Fix: Canonical to desktop version**

```vue [pages/Article.vue]
<script setup lang="ts">
import { useHead } from '@unhead/vue'

useHead({
  link: [{
    rel: 'canonical',
    // Always point to main URL
    href: 'https://mysite.com/article'
  }]
})
</script>
```

For print, use CSS `@media print` instead of separate URLs.

### Session IDs and Click Tracking

Session IDs in URLs create infinite variations.

```
/products?sessionid=abc123
/products?sessionid=xyz789
/products?sessionid=def456
```

**Fix: Don't put session IDs in URLs.** Use cookies. If unavoidable, [block with robots.txt](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/robots-txt):

```robots-txt [public/robots.txt]
User-agent: *
Disallow: /*?sessionid=
Disallow: /*&sessionid=
Disallow: /*?sid=
Disallow: /*&sid=
```

## Finding Duplicate Content

### Google Search Console

[Use the Page indexing report](https://support.google.com/webmasters/answer/7440203) to identify duplicates:

1. Open Search Console
2. Go to "Indexing" → "Pages"
3. Look for:
   - "Duplicate, Google chose different canonical than user"
   - "Duplicate without user-selected canonical"
   - "Alternate page with proper canonical tag"

Click each category to see affected URLs. If Google chose a different canonical than you specified, [conflicting signals exist](https://developers.google.com/search/blog/2019/03/how-to-discover-suggest-google-selected).

**Using URL Inspection:**

1. Enter any URL
2. Check "User-declared canonical" vs "Google-selected canonical"
3. If they differ, Google found stronger signals pointing to a different URL

**View page source (not DevTools)** to verify canonical tags are server-rendered:

```bash
curl https://mysite.com/products?sort=price | grep canonical
```

**Check for canonicalization conflicts:**

- Multiple `rel="canonical"` tags on same page
- Canonical in `<head>`{lang="html"} vs HTTP header
- Canonical points to redirect or noindexed page
- Canonical URL returns 4xx/5xx status

**Test redirect chains:**

```bash
curl -I https://mysite.com/old-url
```

Should show one 301 redirect, not a chain.

### Screaming Frog

[Screaming Frog detects exact and near-duplicate content](https://www.screamingfrog.co.uk/seo-spider/tutorials/how-to-check-for-duplicate-content/):

**Exact duplicates**: Pages with identical HTML (MD5 hash match)

**Near duplicates**: Pages with 90%+ similarity (minhash algorithm)

**Setup:**

1. Enable near duplicates: `Config > Content > Duplicates`
2. Crawl your site
3. Go to "Content" tab
4. Filter by "Exact Duplicates" or "Near Duplicates"

Check these columns:

- `Closest Similarity Match`: Percentage match to most similar page
- `No. Near Duplicates`: Count of similar pages
- `Hash`: MD5 hash for exact duplicate detection

[Screaming Frog](https://screamingfrog.co.uk) auto-excludes nav and footer elements to focus on main content. [Adjust threshold](https://www.screamingfrog.co.uk/seo-spider/user-guide/tabs/#content) if needed (default 90%).

### Manual and Third-Party Tools

Use Google site search to find duplicates manually:

```
site:mysite.com "exact title text"
```

If multiple URLs appear with the same title, inspect their content. A shared title does not prove duplicate content.

**[Siteliner](https://www.siteliner.com/)** is a free tool that crawls up to 250 pages and shows a duplicate content percentage. **[Copyscape](https://www.copyscape.com/)** detects external duplicate content, i.e. other sites copying you. Both are useful for content audits but don't replace Search Console or Screaming Frog for technical SEO.

## Canonical vs 301 Redirect

| When to Use                         | Canonical Tag | 301 Redirect |
| ----------------------------------- | ------------- | ------------ |
| **Need both URLs live**             | ✅ Yes         | ❌ No         |
| **User should see one URL**         | ❌ No          | ✅ Yes        |
| **Products in multiple categories** | ✅ Yes         | ❌ No         |
| **Old page no longer needed**       | ❌ No          | ✅ Yes        |
| **UTM tracking parameters**         | ✅ Yes         | ❌ No         |
| **www vs non-www**                  | ❌ No          | ✅ Yes        |
| **HTTP vs HTTPS**                   | ❌ No          | ✅ Yes        |
| **Moved/renamed pages**             | ❌ No          | ✅ Yes        |

**Canonical tags** are [strong canonicalization signals, not directives](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls). Google may ignore them. Both versions remain accessible. Use for duplicates you need (tracking params, multiple category paths).

**301 redirects** are permanent. Users see the redirect target. They give Google a strong canonicalization signal. Use for outdated or unnecessary URLs.

Use redirects for URLs that should stop serving content. Use canonicals for duplicate pages that remain accessible. Keep destination signals consistent.

## Decision Tree

**Examples:**

- `http://mysite.com` → `https://mysite.com`: **301 redirect**
- `www.mysite.com` → `mysite.com`: **301 redirect**
- `/products?utm_source=twitter` → `/products`: **Canonical tag**
- `/products/shoes` and `/sale/shoes` (same product): **Canonical tag** (one canonical, one alternate)
- `/products?filter=red`: **Choose noindex for exclusion, or canonical for substantially duplicate content**
- `/old-page` → `/new-page`: **301 redirect**

## Common Mistakes

**Mistake 1: Canonicalizing all paginated pages to page 1**

```vue
<!-- ❌ Wrong - hides pages 2+ from search -->
<script setup lang="ts">
import { useHead } from '@unhead/vue'

useHead({
  link: [{ rel: 'canonical', href: 'https://mysite.com/blog' }]
})
</script>
```

[Each paginated page should reference itself](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/pagination#self-referencing-canonical-tags).

**Mistake 2: Using relative canonical URLs**

::code-group
```html [❌ Bad]
<!-- Relative paths work, but Google recommends absolute URLs -->
<link rel="canonical" href="/products/phone">
```

```html [✅ Good]
<link rel="canonical" href="https://mysite.com/products/phone">
```
::

[Google recommends absolute URLs and supports relative paths](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls).

**Mistake 3: Combining canonical with noindex**

::code-group
```vue [❌ Bad]
<script setup lang="ts">
import { useHead, useSeoMeta } from '@unhead/vue'

// Conflicting signals - pick one approach
useHead({
  link: [{ rel: 'canonical', href: 'https://mysite.com/page' }]
})
useSeoMeta({
  robots: 'noindex, follow'
})
</script>
```

```vue [✅ Good - Canonical Only]
<script setup lang="ts">
import { useHead } from '@unhead/vue'

// Use canonical when page should be indexed but consolidated
useHead({
  link: [{ rel: 'canonical', href: 'https://mysite.com/page' }]
})
</script>
```

```vue [✅ Good - Noindex Only]
<script setup lang="ts">
import { useSeoMeta } from '@unhead/vue'

// Use noindex when page should not appear in search
useSeoMeta({
  robots: 'noindex, follow'
})
</script>
```
::

Canonical says "this is a duplicate of X." Noindex says "don't index this." [Pick one](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/canonical-urls).

**Mistake 4: Canonical chains**

```
Page A → canonical → Page B → canonical → Page C
```

A chain dilutes the signal and search engines may not follow it all the way through. Canonical directly to the final target instead.

**Mistake 5: Client-side canonicals in SPAs**

Google can render JavaScript, but rendering may fail. [Put canonical tags in initial HTML where practical](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/rendering). Verify rendered tags when using client-side injection.

## Preventing Duplicate Content

### Configure Vue Router

Choose one trailing-slash policy and declare matching routes. A strict router can treat slash variants as different paths. Use the [Trailing Slashes guide](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/trailing-slashes) for server redirect handling. Test direct loads, query strings, and browser navigation.

### Validate Parameter Values

Prevent infinite URL variations by whitelisting allowed parameter values:

Parse query values at the router or server boundary. Accept only supported single-string values. Vue Router query values can also be null or arrays. Decide whether unsupported values should return an error, redirect, or use a default. Test that policy in your application.

### Block Low-Value Pages

Use [robots.txt](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/robots-txt) to block search results, filtered pages, and admin sections:

```robots-txt [public/robots.txt]
User-agent: *

# Block search results
Disallow: /search?
Disallow: /*?q=
Disallow: /*?query=

# Block filters
Disallow: /*?filter=
Disallow: /*&filter=

# Block session IDs
Disallow: /*?sessionid=
Disallow: /*?sid=
```

Using Nuxt? [Nuxt SEO](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction) handles canonical URLs automatically through site config and route rules. [Learn more about duplicate content in Nuxt →](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers)

## Checklist

::checklist{#vue-duplicate-content}
- Choose one canonical domain (www or non-www, HTTP or HTTPS) and redirect the rest
- Pick one trailing slash format in Vue Router and enforce it site-wide
- Use canonicals for substantially duplicate filters and sorts. Keep distinct pagination self-referencing
- Strip tracking parameters (utm_source, fbclid, gclid) from canonical URLs
- Use absolute URLs in every canonical tag, never relative paths
- Never combine noindex with a canonical tag on the same page
::

## Sitemap

See the full [sitemap](https://nuxtseo.com/sitemap.md) for all pages.
