首页 / Nuxt 4 入门教程 / 服务端状态与跨请求安全

Nuxt 4 入门教程

服务端状态与跨请求安全

本教程共 50 篇 · 第 28 篇 · 更新于 2026-08-08 · 约 8 分钟阅读

NuxtNuxt4服务端跨请求安全Nitrocookie

本节目标:理解 Nuxt 服务端「每个请求彼此隔离」的道理,掌握 cookie/会话与请求级状态的正确写法,避免数据串号。

28-1

前面几章都在聊浏览器端的状态(Pinia、useState)。这一章翻到服务端:那些放在 server/ 目录下的 API 和中间件。它们由 Nuxt 内置的 Nitro 引擎驱动,底层用的是轻量 HTTP 框架 h3。

一个最关键的认知:服务端是「多请求并发」的环境。你的应用上线后,同一时刻可能有一百个用户各发来一个请求。服务器会为每个请求跑一遍你的 handler 代码。所以「状态放在哪、会不会被别的请求看到」,直接关系到安全。

Warning

千万不要在 server/ 目录的文件顶层写 const userState = {} 这种「模块级全局变量」来存请求相关的数据。在长时间运行的服务(或 serverless 实例复用)里,这个对象会被后续所有请求共享,上一个用户的数据会泄漏给下一个用户——这是典型的安全事故。

28-2

h3 的每个请求都会拿到一个 event 对象(类型是 H3Event)。它是「本次请求」的专属信封,里面装着请求信息、响应方法,还可以挂你自己的上下文。正确做法是:把只在「这一次请求内」共享的数据,挂在 event.context 上。

export default defineEventHandler((event) => {
  // 本次请求内,多个 server 函数间共享的数据,挂在这里
  event.context.userId = '123'

  return { hello: 'world' }
})

event.context 是「请求级」的:每个请求各自一份,互不干扰,请求结束就作废。这正是跨请求安全的核心——请求相关的状态,永远随请求走,不进模块全局

Note

在 h3 里,event.context 就是专门给你挂「本次请求上下文」的地方。Server 中间件里算好的东西(比如解析出的用户身份),挂到 event.context,后面的 handler 就能直接读,而不必每个 handler 重复解析。

28-3

服务端中间件(server/middleware/)会在每个请求进来时先跑一遍,很适合在这里解析 token、识别用户,并把结果挂到 event.context,供后续 handler 使用。

export default defineEventHandler((event) => {
  const token = getCookie(event, 'token')
  if (token) {
    // 伪代码:解析 token 得到用户
    event.context.user = parseToken(token)
  }
})
export default defineEventHandler((event) => {
  // 直接读中间件挂好的上下文
  return { user: event.context.user ?? null }
})

这样「鉴权」只写一次,所有接口都能受益。

Warning

服务端中间件如果 return 了一个值,Nitro 会把它当作响应直接返回、终止后续处理。所以鉴权类中间件不要返回值,只往 event.context 上挂数据即可;要拦截请求(如未登录跳转),用 throw createError(...)sendRedirect

28-4

cookie 是服务端识别「这是哪个用户」最常用的手段。h3 提供了一组读/写 cookie 的辅助函数,比原生 event.node.req.headers.cookie 好用得多。

读 cookie:

export default defineEventHandler((event) => {
  const token = getCookie(event, 'token')
  return { hasToken: !!token }
})

写 cookie(比如登录成功后下发会话):

export default defineEventHandler(async (event) => {
  const { username, password } = await readBody(event)
  // 伪代码:校验凭据
  const sessionId = createSession(username)

  setCookie(event, 'sid', sessionId, {
    httpOnly: true,
    sameSite: 'lax',
    path: '/',
  })

  return { ok: true }
})

httpOnly: true 让前端 JS 读不到这个 cookie,能挡掉大部分 XSS 偷 cookie 的攻击;sameSite 有助于防御 CSRF。这些安全选项,生产环境请务必带上。

28-5

前面第 22 章提过:在组件里用 useFetch 调相对路径时,Nuxt 会经 useRequestFetch 自动把浏览器的 cookie 和请求头代理到这次服务端请求(除了 host 这类不该转发的)。

<script setup lang="ts">
// 服务器上执行时,会自动带上用户浏览器的 cookie 去请求 /api/me
const { data } = await useFetch('/api/me')
</script>

如果你不用 useFetch,而是自己 $fetch,又想带上 cookie,就得手动用 useRequestHeaders(['cookie'])

<script setup lang="ts">
const headers = useRequestHeaders(['cookie'])

async function getCurrentUser () {
  return await $fetch('/api/me', { headers })
}
</script>
Warning

把浏览器请求头代理给外部 API 时要格外小心,只转发你确实需要的头。像 hostacceptcontent-lengthx-forwarded-*cf-connecting-ip 这类头不适合转发,乱转发可能引入安全隐患或奇怪的行为。

28-6

一个铁律:密码、API 密钥、数据库凭证这类东西,永远只在服务端出现。在 Nuxt 里,把它们放进 runtimeConfigprivate 段(或环境变量 .env),只在 server/ 代码里读取;绝不要写进客户端组件,也不要通过 API 原样返回给浏览器。

export default defineEventHandler((event) => {
  // 只在服务端读私密配置,安全
  const apiKey = useRuntimeConfig(event).secretApiKey
  return { ok: true }
})
Tip

服务器 handler 里 return 的对象会直接变成 JSON 响应;只 return 你想公开的数据。任何敏感字段,要么不返回,要么先脱敏。记住:只有你显式 return 的东西才会离开服务器,其余都安全留在服务端。

28-7

和浏览器端不同,服务端代码是「来一个请求、执行一遍、返回、结束」,没有 Vue 那套响应式系统,也没有组件挂载/卸载。所以:

  • 不要在 handler 顶层开 setInterval / setTimeout 去「持有状态」,请求结束它们还在跑,且会看到过期数据。
  • 不要在模块顶层存可变的请求数据(前面强调过,会串号)。
  • 需要「跨请求共享但只读」的东西(如配置、连接池),才适合放模块级常量——且必须是不可变或自身线程安全的。

28-8

服务端机制在 Nuxt 3 与 Nuxt 4 中一致,均基于 Nitro + h3。仅目录位置不同:Nuxt 4 用 server/(与 app/ 平级),Nuxt 3 也是 server/,两者在此处相同;Nuxt 3 老项目若用 runtimeConfig,字段结构与 Nuxt 4 一致,private / public 的划分相同。

28-9

服务端状态管理的核心就一句话:请求相关的数据随请求走,挂在 event.context 上,绝不进模块全局。getCookie / setCookie 处理会话,用 runtimeConfig 保管密钥,用中间件统一鉴权。守住「隔离」与「不泄密」两条线,你的 Nuxt 全栈应用才既好用又安全。至此,「数据与状态」这一部分(第 22–28 章)就讲完了,下一部分我们会进入样式与静态资源。

28-6 跨请求安全的实践清单

保障跨请求安全不需要复杂的架构,但需要养成良好的编码习惯。以下是一份简明的实践清单,帮你在日常开发中避免常见的安全陷阱。

首先,永远不要在模块顶层用普通变量存储请求相关的数据。任何在 <script setup> 外、模块作用域里声明的变量,都可能在多个请求间共享。其次,使用 Nuxt 提供的 useStateuseCookieuseRequestHeaders 等组合式函数来管理请求级别的状态,它们会自动处理跨请求隔离。

第三,在编写服务端中间件或 API 路由时,注意从 event 对象获取上下文信息,而不是依赖全局状态。第四,定期审查项目中使用 globalThis 或进程级缓存的代码,确认它们不会泄漏用户数据。遵循这些原则,你的 Nuxt 应用就能在多用户并发访问时保持安全。

28-7 安全编码的检查清单

编写服务端安全的代码需要养成一些习惯。以下是一份简明的检查清单,帮你在日常开发中避免常见的安全问题。

第一,永远不要信任客户端传来的数据。所有来自请求参数、请求体、请求头的值都应该经过验证和清理后再使用。第二,避免在响应里返回不必要的信息。错误消息不要包含堆栈跟踪或系统路径,只返回用户需要知道的提示。

第三,敏感操作(如删除数据、修改密码)要用 POST 或 DELETE 方法,不要用 GET。GET 请求会被浏览器预加载、被搜索引擎爬虫访问,可能触发意外的操作。第四,定期更新依赖版本,修复已知的安全漏洞。Nuxt 和 Nitro 团队会及时发布安全补丁,保持更新是最基本的安全措施。