---
title: "SEO-Friendly URLs in Nuxt · Nuxt SEO"
canonical_url: "https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/url-structure"
last_updated: "2026-07-16T12:00:00.000Z"
meta:
  author: "Harlan Wilton"
  description: "Get slugs, casing, and query params right so file-based routing produces URLs Google indexes cleanly instead of as duplicates."
  "og:description": "Get slugs, casing, and query params right so file-based routing produces URLs Google indexes cleanly instead of as duplicates."
  "og:title": "SEO-Friendly URLs in Nuxt · Nuxt SEO"
---

Nuxt SEO on GitHub

# **SEO-Friendly URLs in Nuxt**

Get slugs, casing, and query params right so file-based routing produces URLs Google indexes cleanly instead of as duplicates.

[Harlan Wilton](https://x.com/harlan-zw)8 mins read Published **Dec 17, 2025** Updated **Jul 16, 2026**

**What you'll learn**

- Use hyphens (not underscores), lowercase, under 60 characters
- Path segments for indexed content, query params for filters/sorting
- Set canonical URLs to consolidate filter variations like `**?sort=price**`

URLs appear in search results before users click. `**/blog/vue-seo-guide**` tells users what to expect; `**/p?id=847**` doesn't. Search engines also use URLs to understand page hierarchy and relevance.

Nuxt's file-based routing generates SEO-friendly URLs automatically from your `**pages/**` directory structure.

## Quick Setup

File-based routing in `**pages/**`:

```
pages/
  blog/
    [slug].vue          → /blog/vue-seo-guide
  products/
    [category]/
      [slug].vue        → /products/phones/iphone-15
```

pages/blog/\[slug].vue

```vue
<script setup lang="ts">
const route = useRoute()
const slug = route.params.slug

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

## URL Formatting Rules

### Hyphens Over Underscores

[**~~Google treats hyphens as word separators~~**](https://developers.google.com/search/docs/crawling-indexing/url-structure). Underscores connect words into single terms.

```
✅ /performance-optimization    → "performance" + "optimization"
❌ /performance_optimization    → "performanceoptimization"
```

**Nuxt file structure:**

```
pages/
  learn-vue-router.vue     ✅ Good
  learn_vue_router.vue     ❌ Bad
```

### Lowercase Only

URLs are case-sensitive. `**/About**`, `**/about**`, and `**/ABOUT**` are different pages. This creates [**~~duplicate content~~**](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/duplicate-content) issues.

```
✅ /about
✅ /products/phones
❌ /About
❌ /products/Phones
```

Use [`**kebabCase**`**~~ from scule~~**](https://github.com/unjs/scule) to generate lowercase slugs automatically.

### Keep URLs Short

URLs under 60 characters perform better in search results. Longer URLs get truncated with ellipsis.

**Length comparison:**

| **URL** | **Length** | **Result** |
| --- | --- | --- |
| `**/blog/vue-seo**` | 14 chars | ✅ Displays fully |
| `**/blog/comprehensive-guide-to-vue-seo-optimization**` | 50 chars | ⚠️ Works but verbose |
| `**/blog/a-comprehensive-guide-to-vue-server-side-rendering-seo-optimization-best-practices**` | 98 chars | ❌ Truncated in results |

**When longer URLs make sense:**

```
✅ /docs/getting-started/installation-guide    (clear hierarchy)
✅ /blog/2025/fixing-vue-hydration-mismatch   (date + topic)
❌ /the-ultimate-comprehensive-complete-guide  (keyword stuffing)
```

### Keywords Near the Start

Google's own guidance is that a [**~~URL should describe the page's content clearly~~**](https://developers.google.com/search/docs/crawling-indexing/url-structure) so users and crawlers can identify the concept at a glance. Front-load the important terms.

```
✅ /vue-router-seo-guide
✅ /seo/vue-best-practices
❌ /guides-and-tutorials-for-seo-in-vue-router
```

But don't sacrifice readability:

```
pages/
  vue-seo/
    [topic].vue          ✅ Natural keyword placement
  vue-seo-guide-vue-router-seo-tutorial.vue  ❌ Keyword stuffing
```

### Avoid Dates (Usually)

Dates in URLs prevent content updates. `**/blog/2024/vue-guide**` becomes outdated when you refresh it in 2025.

```
❌ /blog/2024/vue-router-guide      (looks stale)
❌ /blog/2024/12/17/post-title      (prevents evergreen updates)
✅ /blog/vue-router-guide           (can be updated anytime)
```

**Exception:** Time-sensitive content like news, events, changelogs:

```
pages/
  changelog/
    [year]/
      [month]/
        [slug].vue       → /changelog/2025/12/new-feature
  events/
    2025/
      [slug].vue         → /events/2025/nuxt-conf
  blog/
    [slug].vue           → /blog/vue-router-guide (evergreen)
```

Removing dates lets you republish old posts with new content without changing the URL, so links and rankings built up over time carry forward instead of starting over.

## Path Segments vs Query Parameters

![Path vs Query Parameter Decision](https://nuxtseo.com/images/learn-seo/vue/path-vs-query-decision.svg)

Search engines prefer path segments over query parameters: [**~~static, keyword-based URLs have a higher likelihood of ranking~~**](https://www.searchenginejournal.com/technical-seo/url-parameter-handling/) than parameter-based ones. Query parameters often cause duplicate content.

**Comparison:**

| **Type** | **Example** | **SEO Impact** |
| --- | --- | --- |
| Path segments | `**/products/phones/iphone-15**` | ✅ Clean, indexed, ranks well |
| Query parameters | `**/products?category=phones&id=15**` | ⚠️ Duplicate content risk |
| Mixed | `**/products/phones?sort=price**` | ✅ Path for content, query for filters |

**Problems with query parameters:**

```
/products
/products?sort=price
/products?sort=date
/products?page=2
/products?sort=price&page=2
```

Five URLs, same content. Google [**~~spends crawl time on the duplicates instead of new or updated pages~~**](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls). See [**~~Query Parameters~~**](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/query-parameters) for handling strategies.

### Nuxt Dynamic Routes

Use dynamic segments for content that should be indexed:

```
pages/
  products/
    [category]/
      [slug].vue        → /products/phones/iphone-15
  blog/
    [year]/
      [month]/
        [slug].vue      → /blog/2025/12/nuxt-seo-guide
  docs/
    [section]/
      [page].vue        → /docs/getting-started/installation
```

Use query parameters for sorting, filtering, and pagination instead. Set canonical URLs to consolidate these variations. See the [**~~Query Parameters guide~~**](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/query-parameters) for implementation details including canonical composables and parameter ordering.

## Dynamic Routes

Nuxt's file-based routing creates SEO-friendly URLs automatically. Use descriptive slugs rather than database IDs: `**/products/laptop-pro-15**` ranks better than `**/products/84792**`.

### Basic Dynamic Segments

```
pages/
  blog/
    [slug].vue          → /blog/:slug
```

pages/blog/\[slug].vue

```vue
<script setup lang="ts">
const route = useRoute()
const slug = route.params.slug

// Fetch content based on slug
const { data: post } = await useFetch(`/api/posts/${slug}`)

// Set meta tags
useSeoMeta({
  title: post.value.title,
  description: post.value.excerpt,
  ogUrl: `https://mysite.com/blog/${slug}`
})

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

### Nested Dynamic Routes

```
pages/
  products/
    [category]/
      [slug].vue        → /products/:category/:slug
```

Generates hierarchical URLs:

- `**/products/electronics/laptop**`
- `**/products/clothing/jacket**`

pages/products/\[category]/\[slug].vue

```vue
<script setup lang="ts">
const route = useRoute()
const category = route.params.category
const slug = route.params.slug

const { data: product } = await useFetch(
  `/api/products/${category}/${slug}`
)

useSeoMeta({
  title: `${product.value.name} - ${category}`,
  description: product.value.description
})
</script>
```

### Catch-All Routes

```
pages/
  search/
    [...params].vue     → /search/:params*
```

Matches:

- `**/search/vue-router**`
- `**/search/vue-router/recent**`
- `**/search/vue-router/recent/2025**`

**Important:** Catch-all routes create multiple URLs for similar content. Use canonical tags:

pages/search/\[...params].vue

```vue
<script setup lang="ts">
const route = useRoute()
const query = Array.isArray(route.params.params)
  ? route.params.params[0]
  : route.params.params

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

## Slug Generation

Use [`**kebabCase**`**~~ from scule~~**](https://github.com/unjs/scule) (already a Nuxt dependency):

```ts
import { kebabCase } from 'scule'

kebabCase('Vue Router Guide') // "vue-router-guide"
kebabCase('50% Off Sale!') // "50-off-sale"
```

For international content, [**~~Google supports UTF-8 URLs~~**](https://developers.google.com/search/docs/crawling-indexing/url-structure) but they must be percent-encoded when sent over HTTP. Consider whether your audience expects localized slugs or ASCII-only.

## URL Hierarchy and Internal Linking

URL structure defines your site's hierarchy, and that hierarchy directly affects how [**~~internal links~~**](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/internal-linking) distribute authority. Flat structures (fewer path segments) make pages easier to crawl and link to. Deep nesting (4+ levels) signals low importance to Google.

| **Structure** | **Crawl Depth** | **SEO Impact** |
| --- | --- | --- |
| `**/features/color-extraction**` | 2 levels | Good, easy to reach |
| `**/docs/v2/api/reference/methods/extract**` | 6 levels | Poor, buried content |

Keep important pages within 3 clicks of the homepage. Use hub pages (like `**/features**`) that link to all child pages, creating a clear topic cluster.

When generating pages at scale ([**~~programmatic SEO~~**](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/programmatic-seo)), use URL patterns that include target keywords:

```
pages/
  compare/
    [slug].vue            → /compare/pocketui-vs-css-peeper
  alternatives/
    [slug].vue            → /alternatives/colorzilla
  features/
    [slug].vue            → /features/color-extraction
```

Each pattern targets a different keyword cluster. Keep slugs descriptive: `**/compare/pocketui-vs-css-peeper**` rather than `**/compare/1**`, so both users and crawlers know what the page compares before they open it.

## Testing URL Structure

**View in search results:**

```bash
# Test how Google displays your URLs
site:yoursite.com "nuxt seo"
```

**Check canonicalization:**

Use [**~~Google Search Console URL Inspection~~**](https://search.google.com/search-console) to verify:

- User-declared canonical matches your intent
- Google-selected canonical agrees with your preference
- No conflicting signals from redirects or alternates

## Checklist

**Checklist**

- URLs use hyphens, not underscores, to separate words
- All paths are lowercase
- Indexed content lives on path segments, not query parameters
- Filters and sorting use query parameters with a self-referencing canonical
- Evergreen content skips dates in the URL
- Slugs are descriptive ( `**laptop-pro-15**`, not `**84792**`)

[**The 2026 SEO Checklist for Nuxt & Vue ** Pre-launch setup, post-launch verification, and ongoing monitoring. Interactive checklist with links to every guide.](https://nuxtseo.com/learn-seo/checklist) [Haven't launched yet? Start with the **Pre-Launch Warmup**](https://nuxtseo.com/learn-seo/pre-launch-warmup)

---

### **Related **

[**Trailing Slashes**](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/trailing-slashes)

[**Canonical URLs**](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/canonical-urls)

[**HTTP Redirects**](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers/redirects)

[**Nuxt SEO Utils**](https://nuxtseo.com/docs/seo-utils/getting-started/introduction)

[**Internal Linking**](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/internal-linking)

[**Routes & Rendering** Structure Nuxt routes and pick per-path rendering so search engines can crawl, index, and rank every page.](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering) [**Pagination** Google indexes page 1 and buries the rest when canonicals point the wrong way. Fix pagination in Nuxt with self-referencing canonicals and crawlable links.](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/pagination)

**On this page**

- [Quick Setup](#quick-setup)
- [URL Formatting Rules](#url-formatting-rules)
- [Path Segments vs Query Parameters](#path-segments-vs-query-parameters)
- [Dynamic Routes](#dynamic-routes)
- [Slug Generation](#slug-generation)
- [URL Hierarchy and Internal Linking](#url-hierarchy-and-internal-linking)
- [Testing URL Structure](#testing-url-structure)
- [Checklist](#checklist)