← 返回文章列表
技术

Next.js standalone 上线排障:Nginx、静态资源和来源校验

复盘 Boker 的 Next.js standalone 部署:Nginx 代理、静态资源缺失和后台来源校验。

Boker 在本地能构建、能跑测试,并不代表通过服务器 IP 打开就能正常写文章。2026 年 9 月的首次实际使用里,先遇到页面没有样式,随后又遇到后台保存和正文插图提示“请求来源无效”。这篇按排查顺序记录原因、修复和验证方法。

先弄清谁在提供什么

Boker 不是可以直接交给 Nginx 托管的静态目录。公开文章需要服务端查询数据库,后台需要登录会话、写入 API 和受保护的媒体读取。当前服务器的实际链路是:

浏览器 -> Nginx :80 -> Next.js standalone 127.0.0.1:3000
                              |-> PostgreSQL
                              |-> 持久化上传目录

systemd 管理 Node.js 进程,Nginx 转发全部路径,包括 /_next/static/... 和 /api/media/...。代理必须保留浏览器使用的 Host,并设置 X-Forwarded-Proto,因为应用要知道外部请求究竟从哪个协议和主机进入。

仓库还提供了另一套更规范的发布入口:npm run release:package 从已提交代码生成源码包和 SHA-256 校验文件;安装脚本在独立版本目录安装依赖、构建、迁移,切换 /srv/boker/current 并检查健康状态。当前验收过的服务器尚未改用这套目录布局,而是从项目目录运行 standalone。写部署记录时把两者分开,避免把 /srv/boker/current 的命令直接照搬到当前服务器。

故障一:HTML 有了,CSS 没有

浏览器能打开页面却没有样式时,先看 /_next/static/...css 是否返回 200,而不是马上改 CSS。问题出在 standalone 产物的准备:next build 生成 .next/standalone/server.js,但运行目录还需要公开资源与 .next/static。缺少后者时,HTML 仍能返回,样式和客户端脚本却会 404。

仓库中的 deploy/prepare-standalone.sh 会把 public/ 和 .next/static/ 复制到 standalone 对应位置,并清除构建时可能被复制进去的环境文件。构建后执行该脚本,再启动 standalone,两个 IP 入口的登录页和 CSS 都应返回 200。这个故障提示我们:验证首页状态码不足以证明前端资源完整,至少还要请求一次页面实际引用的 CSS 和 JS。

故障二:本站后台被判为“来源无效”

最初的写入保护把浏览器发来的 Origin 与 SITE_URL 固定比较。SITE_URL 是站点的规范地址;实际使用时,站长可能从内网 IP,也可能从 Tailnet IP 打开同一个 Nginx。浏览器会按当前地址发送 Origin,于是本来同源的保存文章、图片上传和登录请求也被拒绝。

修复分两层。第一层用 Nginx 转发的 Host 和 X-Forwarded-Proto 还原当前外部来源,并要求请求的 Origin 与它完全一致。第二层为生产环境 HTTP 写入设置显式白名单 ADMIN_HTTP_ORIGINS,只允许指定的两个 IP;公开域名的 HTTP 写入和其他 IP 仍返回 403。SITE_URL 继续负责 canonical 和 sitemap,不再承担“所有后台请求从哪里来”的判断。

还有一个容易漏掉的点:旧登录代码只要处于生产环境就给会话 Cookie 加 Secure。浏览器通过 HTTP IP 访问时,这种 Cookie 无法正常用于登录。现在 HTTPS 入口使用 Secure Cookie,明确允许的 HTTP IP 入口按 HTTP 设置。HTTP 本身不提供应用层传输加密,因此这个入口应限定在可信网络;如果将来开放公网域名后台,应配置 HTTPS。

我们怎样确认修复真的到了线上

排查时先用临时生产进程测试,再检查 Nginx 的真实入口。空正文或未登录请求的状态码可以帮助定位流程走到了哪一步:

请求 预期状态 说明
两个 IP 的 /admin/login 和页面 CSS 200 页面与静态资源可读
两个 IP 的匿名文章写入、匿名图片上传 401 来源校验通过,接下来要求登录
两个 IP 的空字段登录请求 422 来源校验通过,字段验证拦下请求
不在白名单内的 HTTP 主机写入 403 来源策略拒绝

这些状态码只能证明对应关卡,不能代替真实账号的操作。最终由站长在浏览器中验证了登录、退出、上传图片、发布、编辑和删除。

给下一次部署留下的清单

当前目录式运行方式的关键顺序是:确认数据库和上传目录备份,安装依赖与迁移,npm run build,bash deploy/prepare-standalone.sh,重启 systemd,再检查 Nginx 与浏览器流程。完整的版本包安装、校验和回退步骤以 deploy/README.md 为准。

每次变更域名、代理配置或访问协议,都要重新核对 Host、X-Forwarded-Proto、ADMIN_HTTP_ORIGINS、Cookie 属性、CSS/JS 请求和媒体权限。代码可以切回旧版本,数据库迁移却不会自动回滚;发布前的数据库及上传目录备份不能省。

Next.js standalone 上线排障:Nginx、静态资源和来源校验 · Boker