---
title: "Duplicate Content SEO in Nuxt"
description: "Duplicate content wastes crawl budget and splits ranking signals. Here's how to find and fix it with canonical tags, redirects, and parameter handling."
canonical_url: "https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/duplicate-content"
last_updated: "2025-12-17"
---

<key-takeaways>

- [67.6% of websites](https://www.womenintechseo.com/knowledge/dealing-with-duplicate-content-canonicalization-in-detail/) have duplicate content issues, diluting ranking signals across multiple URLs
- Google doesn't penalize but picks which version to index (often not the one you want)
- Use 301 redirects for permanent moves, canonical tags for duplicates you need to keep

</key-takeaways>

[67.6% of websites have duplicate content issues](https://www.womenintechseo.com/knowledge/dealing-with-duplicate-content-canonicalization-in-detail/). Same content at different URLs splits ranking signals and wastes crawl budget. Google picks which version to show, and it's often not the one you want.

[Google doesn't penalize duplicate content](https://developers.google.com/search/docs/advanced/guidelines/duplicate-content) unless you're deliberately scraping other sites. But it hurts SEO by diluting link equity across multiple URLs and confusing search engines about which page to rank.

## Common Causes

### URL Variations

**www vs non-www**

`www.mysite.com` and `mysite.com` are [treated as separate sites](https://yoast.com/video/ask-yoast-use-www-or-not/). 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](/learn-seo/nuxt/routes-and-rendering/trailing-slashes).

Nuxt handles these redirects in `nuxt.config.ts`:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  nitro: {
    prerender: {
      autoSubfolderIndex: false
    }
  }
})
```

For HTTPS and www vs non-www redirects, configure at the server level (Netlify, [Vercel](https://vercel.com), [Cloudflare](https://cloudflare.com)) for best performance and SEO.

### Query Parameters

[URL parameters create exponential duplicates](/learn-seo/nuxt/routes-and-rendering/query-parameters). Three filters generate 8 combinations. Add sorting and pagination, and you're looking at hundreds of URLs.

```text
/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:** Set [canonical tags](/learn-seo/nuxt/controlling-crawlers/canonical-urls#filter-and-sort-parameters) to point filter and sort variations to the base URL. Alternatively, [block filtered pages from indexing](/learn-seo/nuxt/controlling-crawlers/meta-tags) with noindex.

### Parameter Order

`?sort=price&filter=red` and `?filter=red&sort=price` are identical content, different URLs.

**Fix:** Enforce consistent parameter order in canonical URLs. See the [Query Parameters guide](/learn-seo/nuxt/routes-and-rendering/query-parameters) for the implementation.

### Tracking Parameters

Analytics params (`utm_source`, `fbclid`, `gclid`) don't change content but create duplicate URLs.

**Fix:** Strip tracking parameters (utm_source, fbclid, gclid) from canonical URLs. See the [Query Parameters guide](/learn-seo/nuxt/routes-and-rendering/query-parameters) for the composable implementation. Better yet, [redirect tracking params at the server level](/learn-seo/nuxt/routes-and-rendering/query-parameters#server-side-parameter-handling) for proper 301 status codes.

### Pagination

[Each paginated page has unique content](/learn-seo/nuxt/routes-and-rendering/pagination). Use self-referencing canonicals; don't point page 2 to page 1. See the [Pagination SEO guide](/learn-seo/nuxt/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">
const siteUrl = useSiteConfig().url

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

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

### Session IDs and Click Tracking

Session IDs in URLs create infinite variations.

```text
/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](/learn-seo/nuxt/controlling-crawlers/robots-txt):

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

Or use the Robots module:

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



</module-card>

## Finding Duplicate Content

### Google Search Console

[Use the Page Indexing report](https://support.google.com/webmasters/answer/12642436) 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>` 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%).

### Site Search

Use Google site search to find duplicates manually:

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

If multiple URLs appear with the same title, you have duplicates.

### Siteliner and Copyscape

**Siteliner**: Free tool that crawls up to 250 pages, shows duplicate content percentage

**Copyscape**: Detects external duplicate content (other sites copying you)

Both useful for content audits but don't replace Search Console or Screaming Frog for technical SEO.

## Canonical vs 301 Redirect

<table>
<thead>
  <tr>
    <th>
      When to Use
    </th>
    
    <th>
      Canonical Tag
    </th>
    
    <th>
      301 Redirect
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <strong>
        Need both URLs live
      </strong>
    </td>
    
    <td>
      ✅ Yes
    </td>
    
    <td>
      ❌ No
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        User should see one URL
      </strong>
    </td>
    
    <td>
      ❌ No
    </td>
    
    <td>
      ✅ Yes
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Products in multiple categories
      </strong>
    </td>
    
    <td>
      ✅ Yes
    </td>
    
    <td>
      ❌ No
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Old page no longer needed
      </strong>
    </td>
    
    <td>
      ❌ No
    </td>
    
    <td>
      ✅ Yes
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        UTM tracking parameters
      </strong>
    </td>
    
    <td>
      ✅ Yes
    </td>
    
    <td>
      ❌ No
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        www vs non-www
      </strong>
    </td>
    
    <td>
      ❌ No
    </td>
    
    <td>
      ✅ Yes
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        HTTP vs HTTPS
      </strong>
    </td>
    
    <td>
      ❌ No
    </td>
    
    <td>
      ✅ Yes
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Moved/renamed pages
      </strong>
    </td>
    
    <td>
      ❌ No
    </td>
    
    <td>
      ✅ Yes
    </td>
  </tr>
</tbody>
</table>

**Canonical tags** are [hints, not directives](https://www.searchenginejournal.com/canonical-vs-301-redirect/383124/). 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. [Pass the same link equity as canonicals](https://seranking.com/blog/redirect-vs-canonical-tag/) but remove the duplicate from the index. Use for outdated or unnecessary URLs.

**Don't combine:** Using both canonical tag and 301 redirect on the same page sends conflicting signals. Pick one.

## Decision Tree

![Duplicate Content Decision Tree](/images/learn-seo/vue/duplicate-content-decision.svg)

**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`: **Noindex + canonical to base URL**
- `/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">
useHead({
  link: [{ rel: 'canonical', href: 'https://mysite.com/blog' }]
})
</script>
```

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

**Mistake 2: Using relative canonical URLs**

```html
<!-- ❌ Wrong - must be absolute -->
<link rel="canonical" href="/products/phone">

<!-- ✅ Correct -->
<link rel="canonical" href="https://mysite.com/products/phone">
```

[Google requires absolute URLs](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls).

**Mistake 3: Combining canonical with noindex**

```vue
<!-- ❌ Conflicting signals -->
<script setup lang="ts">
useHead({
  link: [{ rel: 'canonical', href: 'https://mysite.com/page' }]
})
useSeoMeta({
  robots: 'noindex, follow'
})
</script>
```

Canonical says "this is a duplicate of X." Noindex says "don't index this." [Pick one](https://www.oncrawl.com/technical-seo/use-robots-txt-meta-robots-canonical-tags-correctly/).

**Mistake 4: Canonical chains**

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

[Google may ignore chained canonicals](https://developers.google.com/search/docs/crawling-indexing/canonicalization-troubleshooting). Canonical directly to the final target.

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

Googlebot doesn't execute JavaScript fast enough. Nuxt renders canonical tags on the server by default, so this isn't an issue.

## Preventing Duplicate Content

### Configure Trailing Slashes

Nuxt handles trailing slashes via `nuxt.config.ts`:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  // Force trailing slashes for prerendered routes
  nitro: {
    prerender: {
      autoSubfolderIndex: true
    }
  }
})
```

For dynamic redirects, use [server middleware](/learn-seo/nuxt/controlling-crawlers/redirects#using-server-middleware).

### Validate Parameter Values

Prevent infinite URL variations by whitelisting allowed parameter values:

```ts
const allowedSortValues = ['price', 'name', 'date', 'rating']
const route = useRoute()
const sort = route.query.sort

if (sort && !allowedSortValues.includes(sort as string)) {
  // Redirect to base URL or default sort
  await navigateTo({ query: { ...route.query, sort: undefined } })
}
```

### Block Low-Value Pages

Use [robots.txt](/learn-seo/nuxt/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 tracking params
Disallow: /*?utm_source=
Disallow: /*?fbclid=
Disallow: /*?gclid=

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

Or configure via the Robots module:

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



</module-card>

## Automatic Handling with Nuxt SEO Utils

Nuxt SEO Utils handles canonical URLs, trailing slashes, and parameter normalization automatically through site config:

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



</module-card>

Configure once in `nuxt.config.ts`:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  site: {
    url: 'https://mysite.com',
    trailingSlash: false
  }
})
```

The module automatically:

- Generates canonical URLs with correct trailing slash handling
- Strips tracking parameters from canonicals
- Normalizes URL formats across your site
