Nuxt 3 部署到 Node 服务器的踩坑与备忘

发布于2026-10-12 12:24 阅读14次 Nuxt 3 打包顺利上线却连踩四坑?本文记录一次真实部署过程:默认只监听回环导致 Nginx 502、proxy_pass 末尾斜杠吃掉路径、hydration mismatch 的成因与规避、runtimeConfig 环境变量不生效,附启动脚本与上线自检清单。
## 部署环境
一台 Debian 12 小机器,Node 20 LTS,PM2 做进程守护,前面套 Nginx 反代并签发证书。项目是 Nuxt 3 后台,`npm run build` 一路顺利,`.output` 目录也生成了,但真正上线时连着踩了四个坑。
## 坑一:默认监听 127.0.0.1
Nitro 的 Node 预设默认只监听本机回环,Nginx 反代时直接 `502 Bad Gateway`,日志里什么错误都没有。排查半天才发现监听地址不对。
解决方式是在启动前显式指定:
```bash
NUXT_HOST=0.0.0.0 NUXT_PORT=3000 node .output/server/index.mjs
```
或者在 `nuxt.config.ts` 里配 `nitro: { preset: 'node-server' }` 后通过 PM2 的环境变量注入。**别指望默认值,一定要自己写死。**
## 坑二:Nginx 反代路径错位
```nginx
location / {
proxy_pass http://127.0.0.1:3000;
}
```
这样写是对的;但如果写成 `proxy_pass http://127.0.0.1:3000/;`,Nginx 会把原始路径里的前缀吃掉,静态资源全部 404。`proxy_pass` 末尾带不带斜杠,语义完全不同,这是最容易写错的一处。
另外 WebSocket 必须单独处理,否则 Nuxt 的 HMR 开发模式和部分实时功能会断:
```nginx
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
```
## 坑三:hydration mismatch
本地 `npm run dev` 一切正常,部署后浏览器控制台刷一片黄字:`Hydration completed but contains mismatches`。原因是服务端渲染拿到的数据和客户端首屏不一致——典型场景是把 `Date.now()` 或 `new Date()` 直接渲染进模板,两边时区/时间不同。
解决办法:把时间相关的内容包进 `<ClientOnly>`,或者在 `onMounted` 之后再赋值。原则是**首屏 HTML 必须与服务端渲染结果字节级一致**。
## 坑四:环境变量没进运行时
`.env` 里配的 API 地址在本地生效,上线后读不到。Nuxt 的 `runtimeConfig` 只有在 `nuxt.config.ts` 里正确声明,且构建时把变量注入进去,才能在运行时读取。光有 `.env` 文件不够:
```ts
export default defineNuxtConfig({
runtimeConfig: {
public: { apiBase: process.env.API_BASE || '' }
}
});
```
构建完成后改 `runtimeConfig.public` 的值,不需要重新 build,直接重启进程即可。
## 一份可用的启动脚本
```bash
#!/bin/bash
cd /www/nuxt-app
export NUXT_HOST=0.0.0.0 NUXT_PORT=3000
pm2 start .output/server/index.mjs --name nuxt-app -i 1
pm2 save
```
## 上线后自检清单
1. `ss -tlnp | grep 3000` 确认监听在 0.0.0.0;
2. `curl -I http://127.0.0.1:3000` 绕过 Nginx 直接访问,确认 Node 侧正常;
3. 浏览器开无痕模式,看控制台有没有 hydration 报错;
4. 刷新两次,确认动态数据不会闪。
Nuxt 3 的部署本身不难,难在它把默认值藏得比较深。把这四条记住,基本一次过。