menu
Cars in street

Creating Redirects With Nuxt 3

Dealing with redirects is only a matter of time, at some point you're going to need them. In this post we'll explore different ways of creating redirects in your Nuxt application.

Why You Should Care About Redirects

  • SEO: Redirects are very important when transferring link equity from one website to another if a page moves from the old site to the new.
  • Backlinks: If your site has been around for a while, hopefully people have linked to it. Think of all of the years people have shared your site in forums, emails, or social media. You really want all of those links to keep working, otherwise people clicking on them will bail.
  • Security: If a user doesn't have access to a page a redirect should send them to the login page.

Server-Side Redirects

The best way to use redirects are on the server so you can return proper HTTP status codes. When running Nuxt with Server-Side Rendering (SSR), you can use Server Middleware, Middleware, or Route Rules.

Server Middleware

This is code ran only on a Nuxt server. If you are creating redirects for SEO or backlinks then you don't need client-side redirects. Creating most of your redirects on the server will declutter your client-side experience and keep it performant. In general, only use middleware if you have no other choice.

To create Server Middleware, create a file under /server/middleware. Nuxt will automatically import the file and use it for all routes. Your file should export a function using defineEventHandler. It gets passed an event object that contains all the information you need.

server/middleware/foo.ts
export default defineEventHandler((event) => {
  console.log('New request: ' + getRequestURL(event))
})

H3 Composables

Nuxt's internal server (H3) has a bunch of helper composables built right in! They are really handy for launching redirects, parsing urls, and even working with cookies!

Handling Error Routes

Before we go further I should mention that there is a funky url you will have to contend with, the error route. It's an internal way Nitro/H3 handles sending errors around. You will see hits like this in your middleware handler.

https://my.app/__nuxt_error?url=/foo-bar/&statusCode=404&statusMessage=Page+not+found:+/foo-bar/&message=Page+not+found:+/foo-bar/&stack

You have to make sure to not redirect these types of url hits, otherwise 404 pages (and more) will get messed up.

General Redirect Template

server/middleware/foo.ts
export default defineEventHandler(async (event) => {
  const urlObj = getRequestURL(event)

  // don't touch error routes
  if (urlObj.pathname === '/__nuxt_error') return

  // detect other conditions and redirect to a new url

  await sendRedirect(event, '/some-new-url', 301)
})

Following this pattern, here are some handy redirect rules.

Redirect One Url

// redirects /old-page -> /new-page
if (urlObj.pathname == '/old-page') {
  await sendRedirect(event, '/new-page')
}

Force Lowercase Urls

// redirects /Products -> /products
if (urlObj.pathname.match(/[A-Z]/)) {
  urlObj.pathname = urlObj.pathname.toLowerCase()
  await sendRedirect(event, urlObj.href)
}

Force Ending Slash

// redirects /products -> /products/
// don't redirect files
if (urlObj.pathname.indexOf('.') == -1 && !urlObj.pathname.endsWith('/')) {
  urlObj.pathname += '/'
  await sendRedirect(event, urlObj.href)
}

Map Old Domain To New

// redirects /products -> http://newsite.com/products
const oldDomain = 'www.old-site.net'
const newDomain = 'newsite.com'
if (event.node.req.headers.host == oldDomain) {
  await sendRedirect(event, event.node.req.href.replace(oldDomain, newDomain))
}

Force SSL

// redirects http://cool-site.com -> https://cool-site.com
if (urlObj.protocol === 'http') {
  urlObj.protocol = 'https'
  await sendRedirect(event, urlObj.href)
}

Middleware

The second way to enforce redirects is through route middleware. Here things get a little tricky as it's ran both server-side and client-side. Middleware is located in the /middleware/ directory and is auto imported. If you want middleware to run on every route, just added .global to the filename (ex: foo.global.ts).

Composables

Named Middleware

middleware/fooBar.ts
// This will be named middleeware called 'foo-bar'
export default defineNuxtRouteMiddleware((to, from) => {
  if (to.path !== '/') return navigateTo('/')
})

Use On Pages

page/foo.vue
<script lang="ts">
definePageMeta({
    middleware: ['foo-bar'],
})
</script>

Inline Middleware

layouts/foo.vue
<script setup>
definePageMeta({
  middleware: [
    function (to, from) {
      // do something
    }
  ]
})
</script>

Route Rules (Experimental!)

You can create redirects using simple route rules in your nuxt config file. These rules ARE compatible with Netlify and Vercel!

nuxt.config.js
export default defineNuxtConfig({
  routeRules:{
    '/old-page': { redirect: '/new-page' }, // 307 (temp redirect)
    '/old-page': { redirect: { to: '/new-page', statusCode: 301 } },
    '/old-path/**': { redirect: '/new-page '}, // redirect old-path/(any) to /new-page
  }
})

Native Redirects With Static Sites

Popular hosting providers also provide a way to create redirects within their server environment. Usually this involves adding a specially named file to your repo that they will read when they build your site.

Redirect To Wrapping This Up

Ok, I feel like I've beaten this thing to death. Redirects are a small pain, and just like tupperware magically seem to multiply overnight. With the techniques above you should be able to manage the chaos and keep your site running smoothly.

Holy crap, I can't believe you are still reading this far. Did I miss anything in this post? Did I get anything wrong? Drop me a line in the contact page.