Cloudflare Workers 部署 Astro 踩坑:流式渲染导致页面白屏的排查与修复

Cloudflare Workers 部署 Astro 踩坑:流式渲染导致页面白屏的排查与修复

一、现象

本地 astro dev 一切正常,但 npm run preview(即 wrangler dev)启动后,访问 /admin/login 页面,浏览器收到的是 白屏。更诡异的是:

  • HTTP 状态码是 200 OK
  • 响应体只有 15 字节
  • 内容显示为 [object Object]

而同一环境下的其他页面(如 /favicon.ico)能正常返回完整的 HTML。

二、排查过程

第一步:确认路由匹配

在 Worker entry 层加入了 debug 日志,看 app.render() 的入参:

[worker-entry] 🎯 app.render() 前, pathname: /admin/login routeData: true

路由是匹配到的(routeData: true),说明不是 404 问题。

第二步:追踪中间件

怀疑是中间件拦截后出了问题,在 callMiddleware 中打日志:

[callMiddleware] 📥 nextCalled: true value类型: object isResponse: true
[callMiddleware] ✅ 返回中间件的Response, status: 200

中间件正常返回了 Response 对象,不是这里的问题。

第三步:追踪渲染管道

Astro 的请求处理链是:

app.render()
  → AstroHandler.handle()
    → #actionsAndPages()      // 检查 Actions
    → PagesHandler.handle()   // 页面渲染
      → renderPage()          // 实际渲染

逐层加日志后发现,renderPage() 确实被调用了:

[renderPage] 🎬 被调用!, isAstroComponent: true streaming: true

关键线索:streaming: true

第四步:定位根因

Astro 默认启用流式渲染(streaming: true)。在标准 Node.js 环境中,renderPage() 返回的 Response 的 body 是一个 ReadableStream,浏览器可以正常消费。

但在 Cloudflare Workers 的 workerd 运行时中,这个 ReadableStream 对象在某个环节被错误地序列化——Response 对象被转成了字符串 "[object Object]",导致最终响应的 body 变成了这 15 个字符。

整个过程没有任何报错,日志安静地显示 status: 200,非常具有迷惑性。

三、解决方案

临时修复

在构建产物 dist/server/chunks/worker-entry_*.mjs 中,找到:

const app = createApp();

改为:

const app = createApp({ streaming: false });

重启 wrangler,页面恢复正常。

自动化修复

直接改构建产物不持久——每次 astro build 都会被覆盖。于是写了一个 post-build 脚本 scripts/patch-worker-entry.mjs自动在每次构建后完成以下修补

  1. 禁用 streaming:从 entry.mjs 解析出 chunk 文件名(含 hash),替换 createApp()createApp({ streaming: false })
  2. 注入 Worker env:在 Worker 的 fetch handler 层面将 env 绑定到 globalThis.__env__,供后续中间件和业务代码读取

同时将脚本集成到 package.jsonpreviewdeploy 命令中:

"preview": "npm run build && node scripts/patch-worker-entry.mjs && wrangler dev ...",
"deploy": "npm run build && node scripts/patch-worker-entry.mjs && ... && wrangler deploy"

四、延伸:环境变量安全

排查过程中还发现 wrangler.json 中硬编码了数据库密钥和管理密码。正确的做法是:

  • 敏感变量(TURSO_AUTH_TOKENSHUOSHUO_PASSWORD)→ 在 Cloudflare Pages Dashboard 的 Environment variables 中设置
  • .env 文件已被 .gitignore,仅用于本地开发
  • wrangler.json 中只保留空占位符,不提交真实值

五、总结

环节关键发现
问题表象200 OK + 15 字节 [object Object]
错误方向中间件、路由、认证逻辑全部正常
真正原因workerd 运行时不兼容 ReadableStream 响应体
最小修复createApp({ streaming: false })
长效方案post-build patch 脚本,自动禁用 streaming + 注入 env

教训:在 Cloudflare Workers 上部署 Astro 时,如果遇到奇怪的「200 OK 但 body 是乱码/空/[object Object]」问题,先检查 streaming 是否被启用。这是 workerd 和 Node.js 运行时差异的典型表现。

💬 评论

加载中...