Three kinds of route middleware
Nuxt provides three kinds of route middleware: (1) Anonymous (or inline) route middleware defined directly within a page; (2) Named route middleware placed in the app/middleware/ directory and automatically loaded via asynchronous import when used on a page; (3) Global route middleware placed in the app/middleware/ directory with a .global suffix, which runs on every route change.
Middleware naming normalization
Middleware names are normalized to kebab-case. For example, myMiddleware becomes my-middleware.
Middleware receives route parameters
Route middleware are navigation guards that receive the current route and the next route as arguments. Middleware is defined with defineNuxtRouteMiddleware and receives 'to' and 'from' parameters representing the next and previous routes respectively.
Middleware return values for navigation control
Middleware can return different values to control navigation: returning nothing (simple return or no return at all) does not block navigation and moves to the next middleware or completes route navigation; return navigateTo('/') redirects to the given path and sets redirect code to 302 Found on server-side redirects; return navigateTo('/', { redirectCode: 301 }) redirects and sets code to 301 Moved Permanently; return abortNavigation() stops the current navigation; return abortNavigation(error) rejects the current navigation with an error.
Middleware execution order
Middleware runs in the following order: (1) Global middleware, (2) Page-defined middleware in the order declared. By default, global middleware executes alphabetically based on filename, but can be ordered explicitly by prefixing with numbers like 01.setup.global.ts, 02.analytics.global.ts to ensure specific execution order. Filenames are sorted as strings, not numeric values, so single-digit numbers should be prefixed with 0.
Middleware runs on both server and client
If a site is server-rendered or generated, middleware for the initial page will be executed both when the page is rendered on the server and again on the client. This can be skipped using import.meta.server and import.meta.client checks.
Access route in middleware using to and from parameters
Always use the 'to' and 'from' parameters in middleware to access the next and previous routes. Avoid using the useRoute() composable in middleware because there is no concept of a 'current route' in middleware, as middleware can abort navigation or redirect to a different route. The useRoute() composable will always be inaccurate in this context.
Add middleware dynamically with addRouteMiddleware
Global or named route middleware can be added manually using the addRouteMiddleware() helper function, such as from within a plugin. This allows adding middleware at runtime with the option { global: true } for global middleware.
Set middleware at build time with pages:extend hook
Instead of using definePageMeta on each page, named route middleware can be added within the pages:extend hook in nuxt.config.ts. This hook allows iterating through pages and setting page.meta.middleware programmatically, and will override any middleware set in definePageMeta in the page.
Route middleware vs server middleware
Route middleware run within the Vue part of the Nuxt app and are completely different from server middleware, which run in the Nitro server part of the app despite their similar names.
Middleware example with defineNuxtRouteMiddleware
Example of route middleware using defineNuxtRouteMiddleware: export default defineNuxtRouteMiddleware((to, from) => { if (to.params.id === '1') { return abortNavigation() } if (to.path !== '/') { return navigateTo('/') } })
Page reference of named middleware with definePageMeta
In a page file, named route middleware can be referenced using definePageMeta with the middleware option, either as an array: middleware: ['auth'] or as a string: middleware: 'auth'.
Dynamically add middleware in plugin
Example of adding middleware in a plugin: export default defineNuxtPlugin(() => { addRouteMiddleware('global-test', () => { console.log('this global middleware was added in a plugin and will be run on every route change') }, { global: true }); addRouteMiddleware('named-test', () => { console.log('this named middleware was added in a plugin') }) })
Server middleware execution
After initializing the Nitro server, middleware under server/middleware/ is executed for every request. Middleware can be used for tasks such as authentication, logging, or request transformation. Returning a value from middleware will terminate the request and send the returned value as the response, which should generally be avoided to ensure proper request handling.
App middleware types and execution
In Nuxt, there are three types of middleware: global route middleware, named route middleware, and anonymous (or inline) route middleware. Nuxt executes all global middleware on the initial page load on both server and client, and then again before any client-side navigation. Named and anonymous middleware are executed only on the routes specified in the middleware property of the page (route) meta defined in the corresponding page components. Any redirection on the server will result in a Location: header being sent to the browser; the browser then makes a fresh request to this new location. All application state will be reset when this happens, unless persisted in a cookie.
App middleware environment-specific code
Nuxt middleware runs on both the server and the client. To run certain code in specific environments, split it by using import.meta.client for the client and import.meta.server for the server.
Client-side authentication middleware example
Example of a client-side authentication middleware in app/middleware/authenticated.ts that redirects unauthenticated users to the login page:
```typescript
export default defineNuxtRouteMiddleware(() => {
const { loggedIn } = useUserSession()
if (!loggedIn.value) {
return navigateTo('/login')
}
})
```
Protected home page with definePageMeta example
Example of a protected home page in app/pages/index.vue that uses definePageMeta to apply the authenticated middleware and displays user information:
```vue
<script setup lang="ts">
definePageMeta({
middleware: ['authenticated'],
})
const { user, clear: clearSession } = useUserSession()
async function logout () {
await clearSession()
await navigateTo('/login')
}
</script>
<template>
<div>
<h1>Welcome {{ user.name }}</h1>
<button @click="logout">
Logout
</button>
</div>
</template>
```
Server middleware runs on every request
Nuxt automatically reads any file in the ~~/server/middleware directory to create server middleware. Middleware handlers will run on every request before any other server route to add or check headers, log requests, or extend the event's request object.
Server middleware should not return or respond to request
Middleware handlers should not return anything, close, or respond to the request. They should only inspect or extend the request context or throw an error.
Example server middleware logging requests
```ts
export default defineEventHandler((event) => {
console.log('New request: ' + getRequestURL(event))
})
```
This example shows a server middleware that logs all incoming requests. This file should be placed in server/middleware/log.ts.
Example server middleware extending request context
```ts
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
```
This example shows a server middleware that extends the request context by adding authentication information. This file should be placed in server/middleware/auth.ts.
Example legacy Node.js middleware
```ts
export default fromNodeMiddleware((req, res, next) => {
console.log('Legacy middleware')
next()
})
```
This example shows how to wrap legacy Node.js middleware. This file should be placed in server/middleware/legacy.ts. Modern h3 middleware is preferred. Never combine the next() callback with a legacy middleware that is async or returns a Promise.