Nuxt Middleware Demystified: Auth, Logging, and the Order that Matters
Nuxt middleware lets you run code before a page renders. This guide walks through Nuxt 2 vs Nuxt 3 middleware, shows a practical auth example, explains execution order, and highlights performance trade‑offs.
09 Oct 2025, 10:32 UTC

Why Middleware Matters in Nuxt
When building a server‑side rendered Vue app with Nuxt, you often need logic that runs before a page is rendered—think authentication checks, request logging, or setting global headers. Nuxt’s middleware layer gives you a single, consistent place to put that code. It runs in the Nuxt context, so you can read the request, set headers, redirect, or inject data into the Vue instance.
Nuxt 2 vs Nuxt 3: Where the Files Live
Nuxt 2 stores middleware in middleware/ or inline via the middleware page option. It runs on both the server and client unless you guard it with process.server checks. Nuxt 3, powered by Nitro, splits the runtime into server/middleware (pure server‑side) and middleware/ (client‑side). Both support async functions, but only the server folder can safely await external services without risking client‑side execution.
Execution Order: Global, Layout, Page
Nuxt executes middleware in a strict order:
- Global middleware – any file in
middleware/(Nuxt 2) orserver/middleware/(Nuxt 3) that is referenced innuxt.configorapp.vue. - Layout middleware – defined in
layouts/via themiddlewareoption. - Page middleware – defined in
pages/via themiddlewareoption or inline.
Because the order is predictable, you can chain redirects or data injections safely. Mis‑ordering can cause a redirect to fire after data has already been fetched, leading to flicker or a failed navigation.
Concrete Example: Auth Middleware in Nuxt 3
Below is a minimal authentication middleware that checks for a cookie named auth_token and redirects unauthenticated users to /login. Place this file in server/middleware/auth.ts (or auth.js if you prefer JavaScript).
// server/middleware/auth.ts
export default defineEventHandler((event) => {
const token = getCookie(event, 'auth_token')
if (!token) {
return sendRedirect(event, '/login')
}
// Optionally verify the token here
})
Register it globally in nuxt.config.ts:
export default defineNuxtConfig({
nitro: {
middleware: ['auth']
}
})
Now every route will run this check before rendering. To verify:
- Run
npx nuxt dev(requiressudoif you’re on Linux and using a privileged port). - Open
http://localhost:3000/protected-pagein a browser. - In the browser console, you should see a
401or a redirect to/loginif noauth_tokencookie is present. - Set the cookie via devtools or an API call, then refresh. The protected page should load without redirection.
Because the middleware runs on the server, the cookie check never leaks to the client, keeping your token handling secure.
Trade‑Offs and Limitations
- Performance: Global middleware executes on every request. Heavy I/O or CPU work can add latency. For expensive tasks, consider moving logic to API routes or Nitro serverless functions.
- Client‑Side Exposure: In Nuxt 2, middleware runs on the client unless you guard it. Avoid putting secrets or sensitive logic in files that could be shipped to the browser.
- Async Timeouts: Nitro may time out an async middleware if it awaits a slow external service. Wrap external calls with
Promise.raceor use a timeout helper. - Redirect Pitfalls: Returning a redirect object or calling
context.redirectis required. Failing to do so will render the target page anyway. - Mixed Environments: Mixing client‑only and server‑only logic in the same file can cause confusing bugs. Keep them in separate folders or use naming conventions like
auth.server.tsfor server‑only code.
Actionable Takeaways
1. Choose the right folder. Use server/middleware for anything that must stay server‑only (auth, logging). Use middleware for client‑side tweaks like analytics.
2. Keep it fast. If a middleware needs to hit an external API, cache the result or move the logic to a dedicated API endpoint.
3. Verify execution order. Add simple console.log('global') and console.log('page') statements, then navigate to confirm the order in the server console.
4. Guard sensitive logic. In Nuxt 2, wrap any server‑only code with if (process.server) { … } to prevent accidental client exposure.
5. Test redirects. Use curl -I http://localhost:3000/protected-page to see the Location header when unauthenticated.
By following these guidelines, you can harness Nuxt middleware to build clean, secure, and performant authentication flows, logging systems, and more—all while keeping the codebase organized and maintainable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.