在边缘节点上跑 Astro:一次真实的踩坑记录 封面
返回上一级

在边缘节点上跑 Astro:一次真实的踩坑记录

写作时间:2026-09-12
# EdgeOne
# Astro
# SSR

把一个在本地跑得好好的 Astro 站点部署到边缘节点,然后它「部署成功但页面打不开」——这篇文章记录了从现象到模块加载层的完整排查路径。

现象:502 只发生在动态路由

静态页面一切正常,但所有走 SSR 运行时的路由都返回 502。日志里只有一行模糊的 entry.mjs load failed

第一步是分离变量:把首页改成 prerender = true 走静态直出,动态路由保留。结果静态页 200、动态页 502——问题被锁定在 SSR 运行时的模块加载阶段。

二分:一个最小 ISR 页

与其在完整项目里猜,不如做一个只有十几行的最小探测页:

export const GET = () => {
  return new Response(JSON.stringify({ ok: true, t: Date.now() }), {
    headers: { "content-type": "application/json" },
  };
};

把它挂到 /isr-min,逐个引入项目里的真实依赖。当引入某个包含原生二进制的包时,远端开始报错——本地 Node 能加载,边缘运行时不能。

根因:桩包与路径分隔符

最终定位到两个叠加的问题:

  1. 桩包:产物里出现了十几个只有 package.json 而没有任何 JS 产物的目录。它们在本地的 node_modules 里是完整的,但构建产物的复制环节只搬走了清单文件。
  2. 路径分隔符:Windows 上生成的清单使用 \ 作为分隔符,Linux 运行时按 / 查找,于是「文件明明在包里却找不到」。

教训:跨平台部署问题几乎总是这两类——原生二进制、路径分隔符。先查这两样,再考虑代码。

修复:构建后自检

与其依赖人工检查,把校验做成构建步骤的一部分:

  • 遍历产物里的所有依赖目录,发现「只有 package.json」的桩包就地修复;
  • 最后用一次真实的 import() 加载入口文件,加载失败直接让构建失败。
const mod = await import(entryPath);
if (typeof mod.default !== "function") {
  throw new Error("SSR entry export invalid");
}

边界:prerender 与 ISR 怎么选

维度prerenderISR
首次访问构建期直出回源渲染后缓存
更新时机重新构建到期再生 / 按需 purge
适用内容稳定的首屏列表、详情等可缓存页

静态资源直出的页面必然可用;需要「静态速度 + 动态更新」的页面交给 ISR,并登记好缓存 tag 以便按需失效。

收尾

  • 「部署成功」只代表产物上传成功,不代表运行时能加载;
  • 构建产物自检(真实 import)比任何文档都可靠;
  • 平台差异要在 CI 里暴露,而不是在用户的 502 里暴露。