第三道防线把应用安全送到生产。本节四块内容:按站点形态选部署目标(把 6.3 的 preset 知识变成决策)、多环境配置(2.2 纪律的落地)、CI/CD 流水线(把 9.1 测试与 8.3 预算接进来)、安全四课(响应头、校验、密钥、依赖)。
3.3 的 routeRules 决定"每页怎么渲染",本节决定"整个站点跑在哪":
| 站点形态 | 部署目标 | Nitro preset | 要点 |
|---|---|---|---|
| 纯静态(文档/博客) | 静态托管/CDN | generate 产物 | 零服务器成本,全球分发 |
| 动态 SSR 为主 | Node 服务器/容器 | node-server | 自管运维,可控性最高 |
| 混合渲染 + 弹性需求 | Vercel/Netlify 类 | vercel/netlify | 平台托管函数,预览环境佳 |
| 全球低延迟轻逻辑 | 边缘网络 | cloudflare | 6.3 的边缘约束清单过一遍 |
决策依据回看 1.2 与 6.3:内容变化频率、流量模式、团队运维能力、预算。没有银弹目标,只有匹配——纯静态站硬上 Node 服务器是浪费,重数据库的 SSR 站硬塞边缘是自虐。
容器化是最通用的形态(不锁云厂商):
# 多阶段构建:产物小、攻击面小 FROM node:20-alpine AS build WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-alpine WORKDIR /app COPY --from=build /app/.output ./.output EXPOSE 3000 CMD ["node", ".output/server/index.mjs"]
产物镜像不含源码与 node_modules 全量(Nitro 产物自带裁剪后的依赖),体积通常在一百 MB 内。

流水线配置示例(GitHub Actions 语法,其他平台等价):
name: ci on: [push] jobs: build-and-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm audit --audit-level=high # 第四课:依赖审计 - run: npx vitest run # 单测 - run: npm run build # 构建 - run: npx playwright test # E2E 对产物 - name: 性能预算 run: npx lighthouse http://localhost:3000 --chrome-flags="--headless" | node scripts/check-budget.mjs # 8.3 的预算脚本
2.2 的纪律在此兑现:构建一次,环境变量区分 dev/staging/prod:
# 预发:连预发数据库,开详细日志 NUXT_PUBLIC_API_BASE=https://api.staging.example.com NUXT_DB_PASSWORD=staging-pass node .output/server/index.mjs # 生产:正式数据库,精简日志 NUXT_PUBLIC_API_BASE=https://api.example.com NUXT_DB_PASSWORD=prod-pass
健康检查端点是运维接口,一行代码:
// server/api/health.get.ts:负载均衡与平台的存活探测 export default defineEventHandler(() => ({ status: 'ok', uptime: process.uptime(), version: useRuntimeConfig().public.version, }))
第一课,响应头。Nitro 路由规则统一注入:
export default defineNuxtConfig({ nitro: { routeRules: { '/**': { headers: { 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'DENY', 'Referrer-Policy': 'strict-origin-when-cross-origin', }, }, }, }, })
内容安全策略(CSP)从严起步:先 report-only 观察违规,再逐步收紧 default-src——一上来就严策略会白屏一片。
第二课,服务端校验。前端校验是体验,服务端校验是边界:
// server/api/orders.post.ts:每个字段显式校验类型与范围 const body = await readBody(event) if (typeof body.productId !== 'number' || body.productId <= 0) { throw createError({ statusCode: 400, statusMessage: 'productId 非法' }) } if (typeof body.quantity !== 'number' || body.quantity < 1 || body.quantity > 99) { throw createError({ statusCode: 400, statusMessage: 'quantity 越界' }) }
第三课,密钥复查。发布前在产物里搜密钥(2.2 的自查法),并确认 cookie 的 httpOnly、secure 属性到位。
第四课,依赖审计。流水线卡 high 级漏洞,紧急漏洞走应急分支——安全债的利息比功能债高。
⚠️ 常见坑:环境变量写进 docker 镜像。镜像会进仓库、被人拉取,等于密钥公开。密钥只在运行时注入(编排层的环境变量配置),镜像保持无密。
💡 关键直觉:CI/CD 的本质是"把每次发布要做对的一百件事变成只需要做对一次的配置"——关卡设在流水线里,人的失误被机器的重复性兜住。