Trailing Slashes in Vue
- Pick one URL format, with or without the trailing slash, and enforce it everywhere
- Prefer a permanent HTTP redirect, such as 301 or 308, for a permanent URL change. A Vue Router guard cannot set that status
- Keep canonical URLs, internal links, and sitemap URLs consistent with your chosen format
A trailing slash is the "/" at the end of a URL:
- With trailing slash:
/about/ - Without trailing slash:
/about
If both variants serve the same page, they can create duplicate URLs. Check your host's behavior:
/aboutand/about/are distinct URLs. Google can select one canonical when they serve duplicate or similar content- Duplicate variants can use additional crawl resources. See crawling and indexing
- Analytics grouped by exact URL can split a page across variants
Pick one format and stick to it: redirect the other, and set canonical URLs to match.
Vue Router Configuration
Enforcing Trailing Slashes
Vue Router matches optional trailing slashes by default. Its strict option requires the slash pattern declared in each path. It does not issue an HTTP redirect.
These are alternative router configuration excerpts for an installed Vue Router app. Import your app-owned About, Products, and BlogPost components before using them:
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
Alternatively, declare routes without trailing slashes. Reuse the imports above and your own page component imports:
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
After creating the no-slash router above, this guard normalizes client navigation. The commented alternative belongs to the slash-declared router. Keep the chosen policy and declared paths aligned:
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()
})Server-Side & Edge Redirects
Configure permanent URL redirects in your server or hosting platform when possible. Platform behavior differs: inspect its documented routing configuration instead of assuming a dashboard setting exists. Avoid redirect loops, preserve meaningful query parameters, and exclude assets or endpoints that need another policy.
Check both variants over HTTP, including query-bearing requests. The target must serve the intended page. A client guard cannot prove any of that behavior.
Nginx
Use the Nginx rewrite module documentation within your actual server configuration. Rewrite order, locations, existing directories, and arguments affect the result. Test your redirect and asset paths before deploying a rule.
Apache
Use Apache's redirect and remapping guidance. Rules differ between server and directory contexts. Account for real directories and verify Location, query preservation, assets, and loop termination.
Canonical URLs
In a component of an Unhead-installed Vue app, set the page's preferred URL. This static example belongs only to the About page:
<script setup lang="ts">
import { useHead } from '@unhead/vue'
useHead({
link: [
{
rel: 'canonical',
href: 'https://example.com/about' // or /about/
}
]
})
</script>Redirects and rel=canonical are canonicalization signals. They do not guarantee Google's selection. For SSR or generation, ensure your pipeline serializes the head into the response.
If you choose no trailing slash:
- Redirect
/about/to/aboutwith a permanent HTTP status, such as 301 or 308 - Page
/aboutshould have<link rel="canonical" href=".../about"> - Test that the redirect terminates at the intended URL and preserves meaningful query values
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.
- Pick one URL format, with or without the trailing slash, for the whole site
- Match declared Vue Router paths to that policy; strict mode alone does not redirect
- Verify a permanent HTTP redirect, such as 301 or 308, separately from client navigation
- 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
In Nuxt, site configuration can shape generated URLs. HTTP redirects require their own server or host policy: Trailing Slashes in Nuxt.
Pre-launch setup, post-launch verification, and ongoing monitoring. Interactive checklist with links to every guide.