404 Not Found

404 Not Found


nginx

Middleware

Bob discovered that Alice could directly access the /admin admin panel without any permission checks. Charlie needs a route guard—one that automatically redirects unauthenticated users attempting to access the admin page to the login page, and prevents regular users from accessing admin routes. Nuxt 3's middleware system is the solution.

1. What You'll Learn


2. A True Story of an Administrator

(1) Pain Point: A Critical Vulnerability Due to Lack of Permission Control

Alice typed /admin into her browser and accessed the MegaShop admin panel, where she could view all orders and user data. Bob, as an administrator, found that the interface he saw was exactly the same as that of a regular user, with no distinction whatsoever. Charlie urgently needed route-level access control.

(2) Solution for Routing Middleware

In Nuxt 3, middleware runs before a route transition and can redirect or block access:

TYPESCRIPT
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  if (!isAuthenticated()) return navigateTo('/login')
})

(3) Benefits: Route-Level Access Control

Alice can no longer access /admin directly; if she is not logged in, she is automatically redirected to the login page. Bob's administrator privileges are enforced at the routing level.


3. Three Middleware Patterns

(1) Order of Middleware Execution

100%
flowchart LR
    A[Route Navigation] --> B[Global Middleware 1]
    B --> C[Global Middleware 2]
    C --> D[Named/Inline Middleware]
    D --> E[Page Render]
    D -->|abort| F[Redirect / Abort]
    B -->|abort| F
    C -->|abort| F

(2) Comparison of the Three Models

Dimension Global Middleware Named Middleware Inline Middleware
File Name *.global.ts *.ts (without "global") No file
Scope All Routes Specific Pages Current Page
Definition Method middleware/ directory middleware/ directory within definePageMeta
Reusability ✅ Automatic reuse ✅ Multi-page references ❌ Current page only
Use Cases Global Authentication/Logging Page-Specific Guard Simple One-Time Check

4. Global Middleware

(1) ▶ Example: Global Authentication Middleware

TYPESCRIPT
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
  // Skip auth for public pages
  const publicPages = ['/', '/products', '/about', '/login', '/register']
  if (publicPages.includes(to.path)) return

  const token = useCookie('auth-token')
  if (!token.value) {
    return navigateTo(`/login?redirect=${to.path}`, { redirectCode: 302 })
  }
})

Output:

TEXT
// Execution Successful

(2) ▶ Example: Global Log Middleware

TYPESCRIPT
// middleware/log.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
  console.log(`Navigation: ${from.path} → ${to.path}`)
  // No return = allow navigation
})

Output:

TEXT
// Execution Successful
⚠️ Note: Global middleware is executed in alphabetical order by filename. 01-auth.global.ts runs before 02-log.global.ts. We recommend using a numeric prefix to control the order.


5. Naming Middleware

(1) ▶ Example: Administrator Role Middleware

TYPESCRIPT
// middleware/admin.ts
export default defineNuxtRouteMiddleware((to) => {
  const user = useState('user')

  if (!user.value) {
    return navigateTo(`/login?redirect=${to.path}`)
  }

  if (user.value.role !== 'admin') {
    // Regular user trying to access admin page
    throw createError({
      statusCode: 403,
      message: 'Access denied. Admin role required.'
    })
  }
})

Output:

TEXT
// Execution Successful

(2) ▶ Example: Referencing a named middleware on a page

VUE
<!-- pages/admin/index.vue -->
<template>
  <div>
    <h1>Admin Dashboard</h1>
    <p>Welcome, Bob (Admin)</p>
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: 'admin', // Reference named middleware
  layout: 'sidebar'
})
</script>

Output:

TEXT
// Execution Successful

(3) ▶ Example: Combining Multiple Middleware Components

VUE
<!-- pages/admin/settings.vue -->
<template>
  <div>
    <h1>Admin Settings</h1>
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: ['auth', 'admin'], // Execute in order: auth → admin
  layout: 'sidebar'
})
</script>

Output:

TEXT
// Execution Successful

6. Inline Middleware

(1) ▶ Example: Defining middleware within a page

VUE
<!-- pages/checkout.vue -->
<template>
  <div>
    <h1>Checkout</h1>
    <p>Cart total: ${{ total }} USD</p>
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: defineNuxtRouteMiddleware((to) => {
    // Inline: check if cart has items
    const cart = useState<any[]>('cart')
    if (cart.value.length === 0) {
      return navigateTo('/cart')
    }
  })
})

const cart = useState<any[]>('cart')
const total = computed(() => cart.value.reduce((s, i) => s + i.price, 0))
</script>

Output:

TEXT
// Execution Successful

(1) Choosing Between Inline and Named Middleware

Scenario Choice Reason
Site-wide Authentication Global Check every route
Admin Routes Names Shared by Multiple Admin Pages
Check if shopping cart is not empty Inline Use only on the checkout page
VIP Area Naming May be used on multiple pages
A/B Testing Traffic Splitting Inline One-Time Logic

7. Middleware and SSR

(1) SSR Dual Execution Mechanism

100%
flowchart TB
    A[User navigates to /admin] --> B[Server: middleware executes]
    B -->|redirect| C[Server sends redirect response]
    B -->|allow| D[Server renders page]
    D --> E[Client: middleware executes again]
    E -->|redirect| F[Client-side redirect]
    E -->|allow| G[Client hydrates page]

(1) ▶ Example: Pure Client-Side Middleware

TYPESCRIPT
// middleware/client-only.ts
export default defineNuxtRouteMiddleware((to) => {
  // Skip on server - only check on client
  if (import.meta.server) return

  const localStorage = window.localStorage
  const preferences = JSON.parse(localStorage.getItem('user-preferences') || '{}')

  if (preferences.theme === 'dark') {
    // Client-only logic using browser APIs
    document.documentElement.classList.add('dark')
  }
})

Output:

TEXT
// Execution Successful

(2) Considerations for SSR Middleware

Problem Cause Solution
Middleware runs twice SSR and client each run once Expected behavior; ensure idempotency
Using window/localStorage SSR (no browser environment) if (import.meta.client) Check
Cookies are available in SSR Requests carry cookies Read them using useCookie
navigateTo in SSR Returns a 302 redirect Expected behavior
useState in middleware ✅ Available Suitable for checking login status

8. Comprehensive Example: The MegaShop Permissions Guard System

TYPESCRIPT
// middleware/01-auth.global.ts - Global auth check
export default defineNuxtRouteMiddleware((to) => {
  const publicPaths = ['/', '/products', '/products/**', '/about', '/login', '/register']
  const isPublic = publicPaths.some(path => {
    if (path.includes('')) return to.path.startsWith(path.replace('/', ''))
    return path === to.path
  })

  if (isPublic) return

  const token = useCookie('auth-token')
  if (!token.value) {
    return navigateTo(`/login?redirect=${encodeURIComponent(to.path)}`)
  }
})
TYPESCRIPT
// middleware/admin.ts - Admin role check
export default defineNuxtRouteMiddleware((to) => {
  const user = useCookie('user-data')

  if (!user.value) {
    return navigateTo('/login')
  }

  if ((user.value as any).role !== 'admin') {
    throw createError({ statusCode: 403, message: 'Admin access required' })
  }
})
VUE
<!-- pages/admin/products/index.vue -->
<template>
  <div>
    <h1>Product Management</h1>
    <p>Manage 1 million products</p>
    <!-- Admin product CRUD interface -->
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: ['admin'],
  layout: 'sidebar'
})
</script>

❓ FAQ

Q How is the execution order of middleware controlled?
A Global middleware is sorted alphabetically by filename; we recommend using a numeric prefix (01-xxx.global.ts). Named middleware is executed in the order specified in the array within definePageMeta.
Q Can asynchronous operations be performed in middleware?
A defineNuxtRouteMiddleware itself does not support async. However, you can call synchronous APIs (such as useCookie or useState) within the middleware. If you need to perform asynchronous validation, it is recommended to handle it in a plugin or in the page's setup method.
Q Why is the middleware executed once during SSR and once on the client side?
A During SSR, the middleware checks route permissions to determine whether to render the page; it is executed again during client-side hydration to ensure consistency. The logic for both executions should be idempotent.
Q Do global middleware affect performance?
A All global middleware are executed every time a route is navigated. Keep the middleware logic lightweight, and avoid performing heavy computations or making API calls within global middleware.
Q What happens if a createError is thrown in the middleware?
A During SSR, an HTML error page with the corresponding status code is returned (e.g., a 403 page). On the client side, the Nuxt error page is displayed. You can customize error.vue.
Q How do you pass data between middleware?
A Use useState or the payload property of NuxtApp. const nuxtApp = useNuxtApp()nuxtApp.payload.xxx = data. The next middleware or page can access it.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty: ⭐): Create a middleware named guest.ts that only allows unauthenticated users to access the page (logged-in users are redirected to the home page), and apply it to the /login page.
  2. Advanced Exercise (Difficulty ⭐⭐): Implement a complete authorization system: global auth checks for login + role checks for the "admin" user; the /admin/** route is protected.
  3. Challenge (Difficulty: ⭐⭐⭐): Implement data passing between middleware—retrieve user information in the auth middleware and store it in the NuxtApp payload, then read it directly in the admin middleware to avoid duplicate requests

---|

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏