---
title: "Trailing Slashes in Vue"
description: "Fix duplicate content from /about vs /about/ with Vue Router strict mode, edge redirects, and matching canonical tags."
canonical_url: "https://nuxtseo.com/learn-seo/vue/routes-and-rendering/trailing-slashes"
last_updated: "2026-07-16"
---

<key-takeaways>

- Pick one URL format, with or without the trailing slash, and enforce it everywhere
- Redirect the wrong format at the server or edge layer with a 301; client-side redirects happen too late for crawlers
- Your canonical tag must match your chosen format exactly

</key-takeaways>

A trailing slash is the "/" at the end of a URL:

- With trailing slash: `/about/`
- Without trailing slash: `/about`

Trailing slashes don't directly affect rankings, but they create technical problems:

- Duplicate content: `/about` and `/about/` can serve the same page without canonicalization, forcing search engines to pick which version to index
- Wasted [crawl budget](/learn-seo/vue/controlling-crawlers#improve-organic-traffic) on large sites, where multiple URLs for one page eat into how much a bot crawls
- Split analytics: different URL formats fragment traffic data for the same page

Pick one format and stick to it: [redirect](/learn-seo/vue/controlling-crawlers/redirects) the other, and set [canonical URLs](/learn-seo/vue/controlling-crawlers/canonical-urls) to match.

## Vue Router Configuration

### Enforcing Trailing Slashes

Vue Router has a `strict` mode that enforces exact path matching:

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

const router = createRouter({
  history: createWebHistory(),
  routes: [
    // Define all routes with trailing slashes
    { path: '/about/', component: About },
    { path: '/products/', component: Products },
    { path: '/blog/:slug/', component: BlogPost }
  ],
  strict: true // Enforce exact path matching
})
```

With `strict: true`, `/about` and `/about/` are treated as different routes.

### Removing Trailing Slashes

To remove trailing slashes consistently:

```ts
const router = createRouter({
  history: createWebHistory(),
  routes: [
    // Define all routes without trailing slashes
    { path: '/about', component: About },
    { path: '/products', component: Products },
    { path: '/blog/:slug', component: BlogPost }
  ],
  strict: true
})
```

### Client-Side Redirects

Add a navigation guard to redirect inconsistent URLs:

```ts
router.beforeEach((to, from, next) => {
  // Remove trailing slashes
  if (to.path !== '/' && to.path.endsWith('/')) {
    next({ path: to.path.slice(0, -1), query: to.query, hash: to.hash })
    return
  }

  // Or add trailing slashes
  // if (to.path !== '/' && !to.path.endsWith('/')) {
  //   next({ path: to.path + '/', query: to.query, hash: to.hash })
  //   return
  // }

  next()
})
```

<warning>

Client-side redirects don't send real HTTP 301 status codes to search engines. Use server-side or edge redirects for SEO.

</warning>

## Server-Side & Edge Redirects

For proper SEO, redirects must happen **before** the Vue app loads. Modern hosting platforms (Cloudflare Pages, [Vercel](https://vercel.com), [Netlify](https://netlify.com)) often have a "Trailing Slash" setting in their dashboard or config file, such as `netlify.toml` or `vercel.json`. Use this first, since it handles the redirect at the network edge, which is the fastest method.

### Nginx

Remove trailing slashes:

```nginx
rewrite ^/(.*)/$ /$1 permanent;
```

Add trailing slashes:

```nginx
rewrite ^([^.]*[^/])$ $1/ permanent;
```

### Apache

Remove trailing slashes:

```apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [R=301,L]
```

Add trailing slashes:

```apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ $1/ [R=301,L]
```

## Canonical URLs

Set canonical URLs to indicate your preferred URL format:

```vue
<script setup lang="ts">
import { useHead } from '@unhead/vue'

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

Don't just rely on redirects. Your `rel="canonical"` tag is a strong hint to Google about which version you prefer.

If you choose **no trailing slash**:

- Redirect `/about/` -> `/about` (301)
- Page `/about` should have `<link rel="canonical" href=".../about">`
- Page `/about/` (before redirect) should *technically* also point to `/about` (but the 301 handles this).

Also make sure all internal links (`<RouterLink>` or `<NuxtLink>`) generate the correct format. If your canonical is `/about` but your menu links to `/about/`, you're sending mixed signals.

<checklist id="vue-trailing-slashes">

- Pick one URL format, with or without the trailing slash, for the whole site
- Enforce it in Vue Router with `strict: true`
- Redirect the wrong format with a 301 at the edge or server, not client-side
- Set canonical tags that match your chosen format exactly
- Audit internal links (`<RouterLink>`) so they match your canonical
- Keep sitemap URLs consistent with your chosen format

</checklist>

If you're using Nuxt instead of Vue, it provides automatic trailing slash handling through site config: [Trailing Slashes in Nuxt](/learn-seo/nuxt/routes-and-rendering/trailing-slashes).
