SEO-Friendly URLs in Vue · Nuxt SEO

[NuxtSEO](https://nuxtseo.com/ "Home")

- [Modules](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction)
- [Tools](https://nuxtseo.com/tools)
- [Pro](https://nuxtseo.com/pro)
- [Learn SEO](https://nuxtseo.com/learn-seo/nuxt) [Releases](https://nuxtseo.com/releases)

[1.4K](https://github.com/harlan-zw/nuxt-seo)

[Nuxt SEO on GitHub](https://github.com/harlan-zw/nuxt-seo)

Learn SEO

Master search optimization

Nuxt

 Vue

[SEO Checklist](https://nuxtseo.com/learn-seo/checklist) [Pre-Launch Warmup](https://nuxtseo.com/learn-seo/pre-launch-warmup) [Backlinks & Authority](https://nuxtseo.com/learn-seo/backlinks)

[Mastering Meta](https://nuxtseo.com/learn-seo/vue/mastering-meta)

- [Titles](https://nuxtseo.com/learn-seo/vue/mastering-meta/titles)
- [Meta Description](https://nuxtseo.com/learn-seo/vue/mastering-meta/descriptions)
- [Social Sharing](https://nuxtseo.com/learn-seo/vue/mastering-meta/social-sharing)
- [Schema.org](https://nuxtseo.com/learn-seo/vue/mastering-meta/schema-org)
- [Migrating vue-meta](https://nuxtseo.com/learn-seo/vue/mastering-meta/migrating-vue-meta)
- [Rich Results](https://nuxtseo.com/learn-seo/vue/mastering-meta/rich-results)
- [Image Alt Text](https://nuxtseo.com/learn-seo/vue/mastering-meta/alt-text)

[ Controlling Crawlers](https://nuxtseo.com/learn-seo/vue/controlling-crawlers)

- [Robots.txt](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/robots-txt)
- [Sitemaps](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/sitemaps)
- [Robot Meta Tag](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/meta-tags)
- [Canonical Link Tag](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/canonical-urls)
- [HTTP Redirects](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/redirects)
- [Duplicate Content](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/duplicate-content)
- [llms.txt](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/llms-txt)

[ SPA SEO](https://nuxtseo.com/learn-seo/vue/spa)

- [Prerendering](https://nuxtseo.com/learn-seo/vue/spa/prerendering)
- [Dynamic Rendering](https://nuxtseo.com/learn-seo/vue/spa/dynamic-rendering)
- [Hydration & SEO](https://nuxtseo.com/learn-seo/vue/spa/hydration)

[ Routes & Rendering](https://nuxtseo.com/learn-seo/vue/routes-and-rendering)

- [URL Structure](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/url-structure)
- [Pagination](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/pagination)
- [Trailing Slashes](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/trailing-slashes)
- [Query Parameters](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/query-parameters)
- [Hreflang & i18n](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/i18n)
- [404 Pages](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/404-pages)
- [Dynamic Routes](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/dynamic-routes)
- [Internal Linking](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/internal-linking)
- [Rendering Modes](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/rendering)
- [Programmatic SEO](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/programmatic-seo)
- [Security](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/security)

[ SSR Frameworks](https://nuxtseo.com/learn-seo/vue/ssr-frameworks)

- [Nuxt vs Quasar](https://nuxtseo.com/learn-seo/vue/ssr-frameworks/nuxt-vs-quasar)
- [Custom Vite SSR](https://nuxtseo.com/learn-seo/vue/ssr-frameworks/vite-ssr)
- [VitePress SEO](https://nuxtseo.com/learn-seo/vue/ssr-frameworks/vitepress)

[ Launch & Listen](https://nuxtseo.com/learn-seo/vue/launch-and-listen)

- [Getting Indexed](https://nuxtseo.com/learn-seo/vue/launch-and-listen/going-live)
- [Google Search Console](https://nuxtseo.com/learn-seo/vue/launch-and-listen/search-console)
- [Core Web Vitals](https://nuxtseo.com/learn-seo/vue/launch-and-listen/core-web-vitals)
- [Indexing Issues](https://nuxtseo.com/learn-seo/vue/launch-and-listen/indexing-issues)
- [SEO Monitoring](https://nuxtseo.com/learn-seo/vue/launch-and-listen/seo-monitoring)
- [Site Migration](https://nuxtseo.com/learn-seo/vue/launch-and-listen/site-migration)
- [IndexNow](https://nuxtseo.com/learn-seo/vue/launch-and-listen/indexnow)
- [Debugging](https://nuxtseo.com/learn-seo/vue/launch-and-listen/debugging)
- [AI Search Optimization](https://nuxtseo.com/learn-seo/vue/launch-and-listen/ai-optimized-content)

1. [Learn SEO for Vue](https://nuxtseo.com/learn-seo)
2.
3. [Routes And Rendering](https://nuxtseo.com/learn-seo/vue/routes-and-rendering)
4.
5. [Url Structure](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/url-structure)

# SEO-Friendly URLs in Vue

Create search-optimized URLs using Vue Router. Learn slug formatting, parameter handling, and route patterns that improve rankings.

[![Harlan Wilton](https://avatars.githubusercontent.com/u/5326365?v=4)Harlan Wilton](https://x.com/harlan-zw)8 mins read Published Dec 17, 2025

What you'll learn

- Vue Router requires manual route definitions; design your URL hierarchy intentionally
- Use hyphens as word separators (not underscores). Google treats hyphens as word breaks
- Keep URLs under 60 characters and lowercase only
- Use path segments for content, query parameters for filters and sorting

URLs appear in search results before users click. `/blog/vue-seo-guide` tells users what to expect. `/p?id=847` doesn't. Search engines use URLs to understand page hierarchy and relevance. Well-structured URLs improve click-through rates by up to 15%.

Vue Router gives you full control over URL structure, but that control comes with responsibility. There is no file based routing to generate clean URLs automatically; you define every route manually in your router config. This means you choose the slug patterns, parameter handling, and hierarchy from scratch using `createRouter()` and `createWebHistory()`.

## [Quick Setup](#quick-setup)

router.ts

```
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/blog/:slug',
      component: BlogPost
    },
    {
      path: '/products/:category/:slug',
      component: ProductPage
    }
  ]
})
```

pages/BlogPost.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>
```

For Vue applications, you'll need to [install Unhead manually](https://unhead.unjs.io/guide/getting-started/installation).

## [URL Formatting Rules](#url-formatting-rules)

### [Hyphens Over Underscores](#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"
```

Google's Matt Cutts confirmed in 2011: "We use the words in a URL as a lightweight factor... we can't easily segment at underscores."

**Vue Router implementation:**

```
// ✅ Good
{ path: '/learn-vue-router', component: Guide }

// ❌ Bad
{ path: '/learn_vue_router', component: Guide }
```

### [Lowercase Only](#lowercase-only)

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

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

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

### [Keep URLs Short](#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](#keywords-near-the-start)

[Including keywords in URLs provides a lightweight ranking boost](https://developers.google.com/search/docs/crawling-indexing/url-structure). Front-load important terms.

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

But don't sacrifice readability:

```
// ✅ Natural keyword placement
{ path: '/vue-seo/:topic', component: Guide }

// ❌ Keyword stuffing
{ path: '/vue-seo-guide-vue-router-seo-tutorial', component: Guide }
```

### [Avoid Dates (Usually)](#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:

```
const routes = [
  // News/events - dates make sense
  { path: '/changelog/:year/:month/:slug', component: Changelog },
  { path: '/events/2025/:slug', component: Event },

  // Evergreen content - skip dates
  { path: '/blog/:slug', component: BlogPost },
  { path: '/guides/:slug', component: Guide }
]
```

[Removing dates allows republishing old posts](https://developers.google.com/search/blog/2019/03/help-google-search-know-best-date-for) with new content without changing URLs. a strong SEO strategy.

## [Path Segments vs Query Parameters](#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. [Path segments are indexed and ranked](https://www.searchenginejournal.com/technical-seo/url-parameter-handling/). 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 sees [duplicate content and wastes crawl budget](https://www.seozoom.com/a-guide-to-query-strings-url-parameters-for-the-seo/). See [Query Parameters](https://nuxtseo.com/learn-seo/vue/routes-and-rendering/query-parameters) for handling strategies.

### [Vue Router Path Segments](#vue-router-path-segments)

Use dynamic segments for content that should be indexed:

router.ts

```
const routes = [
  // ✅ Path segments for SEO
  {
    path: '/products/:category/:slug',
    component: Product
  },
  {
    path: '/blog/:year/:month/:slug',
    component: BlogPost
  },
  {
    path: '/docs/:section/:page',
    component: Documentation
  }
]
```

This generates clean URLs:

- `/products/phones/iphone-15`
- `/blog/2025/12/vue-seo-guide`
- `/docs/getting-started/installation`

### [Query Parameters for Filters](#query-parameters-for-filters)

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

## [Dynamic Routes](#dynamic-routes)

[Vue Router's dynamic routes](https://router.vuejs.org/guide/essentials/dynamic-matching.html) create SEO-friendly URLs. Use descriptive slugs rather than database IDs `/products/laptop-pro-15` ranks better than `/products/84792`.

### [Basic Dynamic Segments](#basic-dynamic-segments)

router.ts

```
const routes = [
  {
    path: '/blog/:slug',
    component: BlogPost,
    props: true
  }
]
```

pages/BlogPost.vue

```
<script setup lang="ts">
const props = defineProps<{ slug: string }>()

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

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

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

### [Nested Dynamic Routes](#nested-dynamic-routes)

router.ts

```
const routes = [
  {
    path: '/products/:category/:slug',
    component: Product,
    props: true
  }
]
```

Generates hierarchical URLs:

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

pages/Product.vue

```
<script setup lang="ts">
const props = defineProps<{
  category: string
  slug: string
}>()

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

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

### [Multiple Optional Parameters](#multiple-optional-parameters)

router.ts

```
const routes = [
  {
    path: '/search/:query/:filters?',
    component: SearchResults
  }
]
```

Matches both:

- `/search/vue-router`
- `/search/vue-router/recent`

**Important:** Optional parameters create multiple URLs for similar content. Use canonical tags:

```
const route = useRoute()

useHead({
  link: [{
    rel: 'canonical',
    href: \`https://mysite.com/search/${route.params.query}\`
  }]
})
```

## [Slug Generation](#slug-generation)

Use [`kebabCase` from scule](https://github.com/unjs/scule):

```
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.

## [Server Configuration](#server-configuration)

### [History Mode Requirements](#history-mode-requirements)

Vue Router's History mode requires server configuration. All routes must serve `index.html`:

Nginx

Apache

Express

H3

```
location / {
  try_files $uri $uri/ /index.html;
}
```

```
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>
```

```
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import express from 'express'

const app = express()
const __dirname = dirname(fileURLToPath(import.meta.url))

// Serve static files
app.use(express.static(join(__dirname, 'dist')))

// All routes return index.html
app.get('*', (req, res) => {
  res.sendFile(join(__dirname, 'dist', 'index.html'))
})

app.listen(3000)
```

```
import { join } from 'node:path'
import { defineEventHandler, readFile, sendRedirect } from 'h3'

export default defineEventHandler(async (event) => {
  const path = event.node.req.url

  // Try to serve static file
  try {
    const file = await readFile(join('./dist', path))
    return file
  }
  catch {
    // Fall back to index.html for client-side routing
    return await readFile('./dist/index.html')
  }
})
```

Without this configuration, direct URLs like `/blog/vue-seo` return 404 errors.

## [Testing URL Structure](#testing-url-structure)

**View in search results:**

```
# Test how Google displays your URLs
site:yoursite.com "vue router"
```

**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

## [Using Nuxt?](#using-nuxt)

If you're using Nuxt, file-based routing generates URLs automatically with canonical URLs, sitemaps, and route rules handled for you.

[Learn more about URL structure in Nuxt →](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering)

[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)

---

[Routes & Rendering URL structure and rendering mode determine whether search engines can crawl, index, and rank your Vue application.](https://nuxtseo.com/learn-seo/vue/routes-and-rendering) [Pagination How to implement SEO-friendly pagination in Vue using canonical tags, self-referencing URLs, and proper link structure.](https://nuxtseo.com/learn-seo/vue/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)
- [Server Configuration](#server-configuration)
- [Testing URL Structure](#testing-url-structure)
- [Using Nuxt?](#using-nuxt)

[GitHub](https://github.com/harlan-zw/nuxt-seo) [ Discord](https://discord.com/invite/275MBUBvgP)

### [NuxtSEO](https://nuxtseo.com/ "Home")

- [Getting Started](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction)
- [MCP](https://nuxtseo.com/docs/nuxt-seo/guides/mcp)

Modules

- [Robots](https://nuxtseo.com/docs/robots/getting-started/introduction)
- [Sitemap](https://nuxtseo.com/docs/sitemap/getting-started/introduction)
- [OG Image](https://nuxtseo.com/docs/og-image/getting-started/introduction)
- [Schema.org](https://nuxtseo.com/docs/schema-org/getting-started/introduction)
- [Link Checker](https://nuxtseo.com/docs/link-checker/getting-started/introduction)
- [SEO Utils](https://nuxtseo.com/docs/seo-utils/getting-started/introduction)
- [Site Config](https://nuxtseo.com/docs/site-config/getting-started/introduction)
- [Skew Protection](https://nuxtseo.com/docs/skew-protection/getting-started/introduction)
- [AI Ready](https://nuxtseo.com/docs/ai-ready/getting-started/introduction)

### [NuxtSEO Pro](https://nuxtseo.com/pro "Nuxt SEO Pro")

- [Getting Started](https://nuxtseo.com/pro)
- [Dashboard](https://nuxtseo.com/pro/dashboard)
- [Pro MCP](https://nuxtseo.com/pro/docs/getting-started/mcp-setup)

### [Learn SEO](https://nuxtseo.com/learn-seo "Learn SEO")

Nuxt

- [Mastering Meta](https://nuxtseo.com/learn-seo/nuxt/mastering-meta)
- [Controlling Crawlers](https://nuxtseo.com/learn-seo/nuxt/controlling-crawlers)
- [Launch & Listen](https://nuxtseo.com/learn-seo/nuxt/launch-and-listen)
- [Routes & Rendering](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering)
- [Staying Secure](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/security)

Vue

- [Vue SEO Guide](https://nuxtseo.com/learn-seo/vue)
- [Mastering Meta](https://nuxtseo.com/learn-seo/vue/mastering-meta)
- [Controlling Crawlers](https://nuxtseo.com/learn-seo/vue/controlling-crawlers)
- [SPA SEO](https://nuxtseo.com/learn-seo/vue/spa)
- [SSR Frameworks](https://nuxtseo.com/learn-seo/vue/ssr-frameworks)
- [SEO Checklist](https://nuxtseo.com/learn-seo/checklist)
- [Pre-Launch Warmup](https://nuxtseo.com/learn-seo/pre-launch-warmup)
- [Backlinks & Authority](https://nuxtseo.com/learn-seo/backlinks)

### [Tools](https://nuxtseo.com/tools "SEO Tools")

- [Social Share Debugger](https://nuxtseo.com/tools/social-share-debugger)
- [Robots.txt Generator](https://nuxtseo.com/tools/robots-txt-generator)
- [Meta Tag Checker](https://nuxtseo.com/tools/meta-tag-checker)
- [HTML to Markdown](https://nuxtseo.com/tools/html-to-markdown)
- [XML Sitemap Validator](https://nuxtseo.com/tools/xml-sitemap-validator)
- [Schema.org Validator](https://nuxtseo.com/tools/schema-validator)
- [Keyword Idea Generator](https://nuxtseo.com/tools/keyword-generator)
- [Keyword Research](https://nuxtseo.com/tools/keyword-research)
- [SERP Analyzer](https://nuxtseo.com/tools/serp-analyzer)
- [Domain Rankings](https://nuxtseo.com/tools/domain-rankings)

Copyright © 2023-2026 Harlan Wilton - [MIT License](https://github.com/harlan-zw/nuxt-seo/blob/main/license) · [mdream](https://mdream.dev)