关于 cloudflare-browser-rendering
cloudflare-browser-rendering 是使用 Cloudflare Browser Rendering 构建浏览器自动化工作流的参考技能,涵盖 Puppeteer、Playwright、会话管理及截图、PDF、抓取和测试模式。
主要功能
- 01Puppeteer 与 Playwright 参考
- 02浏览器会话管理
- 03截图与 PDF 工作流
- 04动态网站数据提取
- 05表单与前端测试自动化
- 06Cloudflare Workers 配置指南
适用场景
- 01捕获网页截图
- 02将网页生成 PDF
- 03提取动态网站数据
- 04自动执行表单和前端测试
MCP 相关
该技能介绍 Playwright MCP 支持及 Playwright MCP 服务端,并说明用于构建浏览器自动化工作流的 Cloudflare Browser Rendering API。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ovachiever/ droid-tings --skill cloudflare-browser-rendering安装后在 Claude Code 中输入/cloudflare-browser-rendering确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.md.claude-plugin/plugin.jsonreferences/common-errors.mdpricing-and-limits.mdpuppeteer-vs-playwright.mdsession-management.mdscripts/check-versions.shtemplates/ai-enhanced-scraper.tsbasic-screenshot.tspdf-generation.tsplaywright-example.tsscreenshot-with-kv-cache.tssession-reuse.tsweb-scraper-basic.tsweb-scraper-batch.tswrangler-browser-config.jsonc详细介绍
Cloudflare Browser Rendering - 完整参考
用于借助 Cloudflare Browser Rendering 构建浏览器自动化工作流的生产就绪知识领域。
状态:生产就绪 ✅ 上次更新:2025-11-23 依赖项:cloudflare-worker-base(用于 Worker 设置) 最新版本:@cloudflare/[email protected](2025 年 7 月)、@cloudflare/[email protected](Playwright v1.55 于 2025 年 9 月正式发布)、[email protected]
近期更新(2025 年):
- 2025 年 9 月:Playwright v1.55 正式发布,支持 Stagehand 框架(Workers AI),/links 新增 excludeExternalLinks 参数
- 2025 年 8 月:计费正式启用(8 月 20 日),本地开发中的 /sessions 端点,X-Browser-Ms-Used 标头
- 2025 年 7 月:Playwright v1.54.1 + MCP v0.0.30,支持 Playwright 本地开发([email protected]+),同步 Puppeteer v22.13.1,/content 返回标题,/json 新增 custom_ai 参数,/screenshot 默认视口为 1920x1080
- 2025 年 6 月:自动附带 Web Bot Auth 标头
- 2025 年 4 月:推出 Playwright 支持,引入免费套餐
目录
快速入门(5 分钟)
1. 添加 Browser Binding
wrangler.jsonc:
{
"name": "browser-worker",
"main": "src/index.ts",
"compatibility_date": "2023-03-14",
"compatibility_flags": ["nodejs_compat"],
"browser": {
"binding": "MYBROWSER"
}
}
为什么需要 nodejs_compat? Browser Rendering 需要 Node.js API 和 polyfill。
2. 安装 Puppeteer
npm install @cloudflare/puppeteer
3. 截取第一张屏幕截图
import puppeteer from "@cloudflare/puppeteer";
interface Env {
MYBROWSER: Fetcher;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { searchParams } = new URL(request.url);
const url = searchParams.get("url") || "https://example.com";
// Launch browser
const browser = await puppeteer.launch(env.MYBROWSER);
const page = await browser.newPage();
// Navigate and capture
await page.goto(url);
const screenshot = await page.screenshot();
// Clean up
await browser.close();
return new Response(screenshot, {
headers: { "content-type": "image/png" }
});
}
};
4. 部署
npx wrangler deploy
在此测试:https://your-worker.workers.dev/?url=https://example.com
重要:
- 始终将
env.MYBROWSER传递给puppeteer.launch()(不能为 undefined) - 完成后始终调用
browser.close()(或使用browser.disconnect()以复用会话) - 使用
nodejs_compat兼容性标志
Browser Rendering 概览
什么是 Browser Rendering?
Cloudflare Browser Rendering 在 Cloudflare 全球网络上提供无头 Chromium 浏览器。使用 Puppeteer 和 Playwright 等熟悉的工具自动执行浏览器任务:
- 屏幕截图 - 捕获网页的视觉快照
- 生成 PDF - 将 HTML/URL 转换为 PDF
- 网页抓取 - 从动态网站提取内容
- 测试 - 自动执行前端测试
- 爬取 - 导航多页工作流
两种集成方式
| 方式 | 适用场景 | 复杂度 |
|---|---|---|
| Workers Bindings | 复杂自动化、自定义工作流、会话管理 | 高级 |
| REST API | 简单的屏幕截图/PDF 任务 | 简单 |
本文档介绍 Workers Bindings(使用完整 Puppeteer/Playwright API 的高级方式)。
Puppeteer 与 Playwright
| 特性 | Puppeteer | Playwright |
|---|---|---|
| API 熟悉度 | 最受欢迎 | 使用日益广泛 |
| 软件包 | @cloudflare/[email protected] | @cloudflare/[email protected] |
| 会话管理 | ✅ 高级 API | ⚠️ 基础 |
| 浏览器支持 | 仅 Chromium | 仅 Chromium(尚不支持 Firefox/Safari) |
| 适用场景 | 屏幕截图、PDF、抓取 | 测试、前端自动化 |
建议:大多数情况下使用 Puppeteer。如果你已经在测试中使用 Playwright,那么它是理想选择。
Puppeteer API 参考
核心 API(完整参考:https://pptr.dev/api/):
全局函数:
puppeteer.launch(env.MYBROWSER, options?)- 启动新浏览器(重要:必须传入绑定)puppeteer.connect(env.MYBROWSER, sessionId)- 连接到现有会话puppeteer.sessions(env.MYBROWSER)- 列出正在运行的会话puppeteer.history(env.MYBROWSER)- 列出最近的会话(打开 + 已关闭)puppeteer.limits(env.MYBROWSER)- 检查账户限制
浏览器方法:
browser.newPage()- 创建新标签页(优先于启动新浏览器)browser.sessionId()- 获取会话 ID,以便重复使用browser.close()- 终止会话browser.disconnect()- 保持会话运行,以便重复使用browser.createBrowserContext()- 隔离的隐身上下文(使用独立的 Cookie/缓存)
页面方法:
page.goto(url, { waitUntil, timeout })- 导航(动态内容使用"networkidle0")page.screenshot({ fullPage, type, quality, clip })- 捕获图像page.pdf({ format, printBackground, margin })- 生成 PDFpage.evaluate(() => ...)- 在浏览器中执行 JS(提取数据、XPath 变通方案)page.content()/page.setContent(html)- 获取/设置 HTMLpage.waitForSelector(selector)- 等待元素page.type(selector, text)/page.click(selector)- 表单交互
关键模式:
// Must pass binding
const browser = await puppeteer.launch(env.MYBROWSER); // ✅
// const browser = await puppeteer.launch(); // ❌ Error!
// Session reuse for performance
const sessions = await puppeteer.sessions(env.MYBROWSER);
const freeSessions = sessions.filter(s => !s.connectionId);
if (freeSessions.length > 0) {
browser = await puppeteer.connect(env.MYBROWSER, freeSessions[0].sessionId);
}
// Keep session alive
await browser.disconnect(); // Don't close
// XPath workaround (not directly supported)
const data = await page.evaluate(() => {
return new XPathEvaluator()
.createExpression("/html/body/div/h1")
.evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE)
.singleNodeValue.innerHTML;
});
Playwright API 参考
状态:GA(2025 年 9 月)- 支持 Playwright v1.55、MCP v0.0.30 和本地开发([email protected]+)
安装:
npm install @cloudflare/playwright
配置要求(2025 年更新):
{
"compatibility_flags": ["nodejs_compat"],
"compatibility_date": "2025-09-15" // Required for Playwright v1.55
}
基本用法:
import { chromium } from "@cloudflare/playwright";
const browser = await chromium.launch(env.BROWSER);
const page = await browser.newPage();
await page.goto("https://example.com");
const screenshot = await page.screenshot();
await browser.close();
Puppeteer 与 Playwright 对比:
- 导入:
puppeteer与来自 "@cloudflare/playwright" 的{ chromium } - 会话 API:Puppeteer 提供高级会话管理(sessions/history/limits),Playwright 提供基础功能
- 自动等待:Playwright 内置自动等待,Puppeteer 需要手动调用
waitForSelector() - MCP 支持:Playwright MCP v0.0.30(2025 年 7 月),提供 Playwright MCP 服务
建议:需要重复使用会话时使用 Puppeteer。迁移现有测试或需要 MCP 集成时使用 Playwright。
官方文档:https://developers.cloudflare.com/browser-rendering/playwright/
会话管理
原因:启动新浏览器耗时较长,且会占用并发限额。重复使用会话可加快响应、降低并发用量并提升资源利用率。
会话重复使用模式(关键)
async function getBrowser(env: Env): Promise<Browser> {
const sessions = await puppeteer.sessions(env.MYBROWSER);
const freeSessions = sessions.filter(s => !s.connectionId);
if (freeSessions.length > 0) {
try {
return await puppeteer.connect(env.MYBROWSER, freeSessions[0].sessionId);
} catch (e) {
console.log("Failed to connect, launching new browser");
}
}
return await puppeteer.launch(env.MYBROWSER);
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const browser = await getBrowser(env);
try {
const page = await browser.newPage();
await page.goto("https://example.com");
const screenshot = await page.screenshot();
await browser.disconnect(); // ✅ Keep alive for reuse
return new Response(screenshot, {
headers: { "content-type": "image/png" }
});
} catch (error) {
await browser.close(); // ❌ Close on error
throw error;
}
}
};
关键规则:
- ✅
browser.disconnect()- 保持会话运行,以便重复使用 - ❌
browser.close()- 仅在出错或确实不再需要时调用 - ✅ 始终处理连接失败
浏览器上下文(Cookie/缓存隔离)
使用 browser.createBrowserContext() 共享浏览器,同时隔离 Cookie/缓存:
const browser = await puppeteer.launch(env.MYBROWSER);
const context1 = await browser.createBrowserContext(); // User 1
const context2 = await browser.createBrowserContext(); // User 2
const page1 = await context1.newPage();
const page2 = await context2.newPage();
// Separate cookies/cache per context
多标签页模式
❌ 不推荐:为 10 个 URL 启动 10 个浏览器(浪费并发额度)
✅ 推荐:使用 Promise.all() + browser.newPage(),在 1 个浏览器中打开 10 个标签页
const browser = await puppeteer.launch(env.MYBROWSER);
const results = await Promise.all(
urls.map(async (url) => {
const page = await browser.newPage();
await page.goto(url);
const data = await page.evaluate(() => ({ title: document.title }));
await page.close();
return { url, data };
})
);
await browser.close();
常见模式
使用 KV 缓存截图
缓存截图以减少浏览器用量并提升性能:
interface Env {
MYBROWSER: Fetcher;
CACHE: KVNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { searchParams } = new URL(request.url);
const url = searchParams.get("url");
if (!url) return new Response("Missing ?url parameter", { status: 400 });
const normalizedUrl = new URL(url).toString();
// Check cache first
let screenshot = await env.CACHE.get(normalizedUrl, { type: "arrayBuffer" });
if (!screenshot) {
const browser = await puppeteer.launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto(normalizedUrl);
screenshot = await page.screenshot();
await browser.close();
// Cache for 24 hours
await env.CACHE.put(normalizedUrl, screenshot, { expirationTtl: 60 * 60 * 24 });
}
return new Response(screenshot, { headers: { "content-type": "image/png" } });
}
};
AI 增强型抓取
将 Browser Rendering 与 Workers AI 结合,以提取结构化数据:
interface Env {
MYBROWSER: Fetcher;
AI: Ai;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { searchParams } = new URL(request.url);
const url = searchParams.get("url");
// Scrape page content
const browser = await puppeteer.launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto(url!, { waitUntil: "networkidle0" });
const bodyContent = await page.$eval("body", el => el.innerHTML);
await browser.close();
// Extract structured data with AI
const response = await env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
messages: [{
role: "user",
content: `Extract product info as JSON from this HTML. Include: name, price, description.\n\nHTML:\n${bodyContent.slice(0, 4000)}`
}]
});
return Response.json({ url, product: JSON.parse(response.response) });
}
};
其他常见模式:生成 PDF(page.pdf())、结构化抓取(page.evaluate())、表单自动化(page.type() + page.click())。请参阅随附的 templates/ 目录。
定价与限额
计费 GA:2025 年 8 月 20 日
免费套餐:每天 10 分钟、3 个并发、每分钟启动 3 次、超时 60 秒
付费套餐:每月包含 10 小时(之后每小时 $0.09)、平均并发 10 个(之后每个浏览器 $2.00)、每分钟启动 30 次、超时 60 秒至 10 分钟
并发量计算:按每日峰值用量的月平均值计算(例如,平均 15 个浏览器 = (15 - 包含的 10 个) × $2.00 = 每月 $10.00)
速率限制:按秒强制执行(180 次请求/分钟 = 3 次请求/秒,不支持突发)。启动前请检查 puppeteer.limits(env.MYBROWSER):
const limits = await puppeteer.limits(env.MYBROWSER);
if (limits.allowedBrowserAcquisitions === 0) {
const delay = limits.timeUntilNextAllowedBrowserAcquisition || 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
已知问题预防
此技能可预防 6 个已记录的问题:
问题 #1:不支持 XPath 选择器
错误:“不支持 XPath 选择器”或选择器失败
来源: https://developers.cloudflare.com/browser-rendering/faq/#why-cant-i-use-an-xpath-selector-when-using-browser-rendering-with-puppeteer
原因: XPath 会给 Workers 带来安全风险
**预防措施:**使用 CSS 选择器,或使用 XPathEvaluator 调用 page.evaluate()
解决方案:
// ❌ Don't use XPath directly (not supported)
// await page.$x('/html/body/div/h1')
// ✅ Use CSS selector
const heading = await page.$("div > h1");
// ✅ Or use XPath in page.evaluate()
const innerHtml = await page.evaluate(() => {
return new XPathEvaluator()
.createExpression("/html/body/div/h1")
.evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE)
.singleNodeValue.innerHTML;
});
问题 #2:未传递浏览器绑定
错误:“无法读取 undefined 的属性(读取 'fetch')”
来源: https://developers.cloudflare.com/browser-rendering/faq/#cannot-read-properties-of-undefined-reading-fetch
**原因:**调用 puppeteer.launch() 时未传入浏览器绑定
**预防措施:**始终将 env.MYBROWSER 传递给 launch
解决方案:
// ❌ Missing browser binding
const browser = await puppeteer.launch(); // Error!
// ✅ Pass binding
const browser = await puppeteer.launch(env.MYBROWSER);
问题 #3:浏览器超时(60 秒)
**错误:**浏览器在 60 秒后意外关闭
来源: https://developers.cloudflare.com/browser-rendering/platform/limits/#note-on-browser-timeout
**原因:**默认超时时间为 60 秒无活动
**预防措施:**使用 keep_alive 选项将时间延长至最多 10 分钟
解决方案:
// Extend timeout to 5 minutes for long-running tasks
const browser = await puppeteer.launch(env.MYBROWSER, {
keep_alive: 300000 // 5 minutes = 300,000 ms
});
**注意:**如果在指定时间内没有 devtools 命令,浏览器就会关闭。
问题 #4:达到并发限制
错误:“超出速率限制”或无法启动新浏览器 来源: https://developers.cloudflare.com/browser-rendering/platform/limits/ **原因:**超出并发浏览器限制(免费版为 3 个,付费版为 10-30 个) **预防措施:**复用会话,使用标签页而非多个浏览器,并在启动前检查限制
解决方案:
// 1. Check limits before launching
const limits = await puppeteer.limits(env.MYBROWSER);
if (limits.allowedBrowserAcquisitions === 0) {
return new Response("Concurrency limit reached", { status: 429 });
}
// 2. Reuse sessions
const sessions = await puppeteer.sessions(env.MYBROWSER);
const freeSessions = sessions.filter(s => !s.connectionId);
if (freeSessions.length > 0) {
const browser = await puppeteer.connect(env.MYBROWSER, freeSessions[0].sessionId);
}
// 3. Use tabs instead of multiple browsers
const browser = await puppeteer.launch(env.MYBROWSER);
const page1 = await browser.newPage();
const page2 = await browser.newPage(); // Same browser, different tabs
问题 #5:本地开发请求大小限制
**错误:**在 wrangler dev 中,请求大小超过 1MB 时失败
来源: https://developers.cloudflare.com/browser-rendering/faq/#does-local-development-support-all-browser-rendering-features
**原因:**本地开发的限制
**预防措施:**在浏览器绑定中使用 remote: true 进行本地开发
解决方案:
// wrangler.jsonc for local development
{
"browser": {
"binding": "MYBROWSER",
"remote": true // Use real headless browser during dev
}
}
问题 #6:始终触发机器人防护
**错误:**网站将请求拦截为机器人流量 来源: https://developers.cloudflare.com/browser-rendering/faq/#will-browser-rendering-bypass-cloudflares-bot-protection **原因:**Browser Rendering 请求始终会被识别为机器人 **预防措施:**无法绕过;如果要抓取自己的区域,请创建 WAF 跳过规则
解决方案:
// ❌ Cannot bypass bot protection
// Requests will always be identified as bots
// ✅ If scraping your own Cloudflare zone:
// 1. Go to Security > WAF > Custom rules
// 2. Create skip rule with custom header:
// Header: X-Custom-Auth
// Value: your-secret-token
// 3. Pass header in your scraping requests
// Note: Automatic headers are included:
// - cf-biso-request-id
// - cf-biso-devtools
生产环境检查清单
将 Browser Rendering Workers 部署到生产环境之前:
配置
- 已在 wrangler.jsonc 中配置浏览器绑定
- 已启用 nodejs_compat 标志(Browser Rendering 必需)
- 如果任务耗时超过 60 秒,已设置保活超时时间
- 如有需要,已为本地开发启用远程绑定
错误处理
- 已为速率限制实现重试逻辑
- 已处理 page.goto() 的超时情况
- 已在 try-finally 块中执行浏览器清理
- 已在启动浏览器前检查并发限制
- 浏览器不可用时可优雅降级
性能
- 已为高流量路由实现会话复用
- 使用多个标签页而非多个浏览器
- 使用无痕上下文隔离会话
- 使用 KV 缓存缓存重复生成的屏幕截图/PDF
- 使用批量操作最大限度地提高浏览器利用率
监控
- 记录浏览器会话 ID,便于调试
- 跟踪浏览器运行时长,用于估算费用
- 使用 puppeteer.limits() 监控并发使用情况
- 对速率限制错误发出警报
- 在 https://dash.cloudflare.com/?to=/:account/workers/browser-rendering 上监控仪表板
安全性
- 输入验证:验证 URL(防止 SSRF)
- 超时限制:防止滥用
- 速率限制:限制公共端点的请求频率
- 身份验证:保护敏感的抓取端点
- WAF 规则:如果抓取自己的区域,请配置 WAF 规则
测试
- 测试截图捕获:使用各种页面尺寸
- 测试 PDF 生成:使用自定义 HTML
- 测试抓取:使用动态内容(networkidle0)
- 测试错误场景:无效 URL、超时
- 负载测试:测试并发限制
错误处理最佳实践
生产环境模式:使用 try-catch 并进行适当的清理:
async function withBrowser<T>(env: Env, fn: (browser: Browser) => Promise<T>): Promise<T> {
let browser: Browser | null = null;
try {
// 1. Check limits before launching
const limits = await puppeteer.limits(env.MYBROWSER);
if (limits.allowedBrowserAcquisitions === 0) {
throw new Error("Rate limit reached");
}
// 2. Try session reuse first
const sessions = await puppeteer.sessions(env.MYBROWSER);
const freeSessions = sessions.filter(s => !s.connectionId);
browser = freeSessions.length > 0
? await puppeteer.connect(env.MYBROWSER, freeSessions[0].sessionId)
: await puppeteer.launch(env.MYBROWSER);
// 3. Execute user function
const result = await fn(browser);
// 4. Disconnect (keep alive)
await browser.disconnect();
return result;
} catch (error) {
// 5. Close on error
if (browser) await browser.close();
throw error;
}
}
关键原则:检查限制 → 重用会话 → 执行 → 成功时断开连接,出错时关闭
使用随附资源
模板(templates/)
适用于常见模式的即用型代码模板:
basic-screenshot.ts- 最简截图示例screenshot-with-kv-cache.ts- 使用 KV 缓存的截图pdf-generation.ts- 从 HTML 或 URL 生成 PDFweb-scraper-basic.ts- 基本网页抓取模式web-scraper-batch.ts- 批量抓取多个 URLsession-reuse.ts- 通过重用会话提升性能ai-enhanced-scraper.ts- 使用 Workers AI 进行抓取playwright-example.ts- Playwright 替代方案示例wrangler-browser-config.jsonc- 浏览器绑定配置
用法:
# Copy template to your project
cp ~/.claude/skills/cloudflare-browser-rendering/templates/basic-screenshot.ts src/index.ts
参考文档(references/)
深入说明文档:
session-management.md- 完整的会话重用指南pricing-and-limits.md- 详细的定价说明common-errors.md- 所有已知问题及解决方案puppeteer-vs-playwright.md- 功能比较和迁移指南
何时加载: 实现高级模式或排查特定问题时,可参考这些文档。
依赖项
必需:
@cloudflare/[email protected]- 适用于 Workers 的 Puppeteer[email protected]+- Cloudflare CLI
可选:
@cloudflare/[email protected]- 适用于 Workers 的 Playwright(替代方案)@cloudflare/[email protected]+- TypeScript 类型
相关技能:
cloudflare-worker-base- 使用 Hono 配置 Workercloudflare-kv- 用于截图的 KV 缓存cloudflare-r2- 用于存储生成文件的 R2cloudflare-workers-ai- AI 增强的抓取
官方文档
- Browser Rendering 文档:https://developers.cloudflare.com/browser-rendering/
- Puppeteer API:https://pptr.dev/api/
- Playwright API:https://playwright.dev/docs/api/class-playwright
- Cloudflare Puppeteer 分支:https://github.com/cloudflare/puppeteer
- Cloudflare Playwright 分支:https://github.com/cloudflare/playwright
- 定价:https://developers.cloudflare.com/browser-rendering/platform/pricing/
- 限制:https://developers.cloudflare.com/browser-rendering/platform/limits/
软件包版本(已于 2025-10-22 验证)
{
"dependencies": {
"@cloudflare/puppeteer": "^1.0.4"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20251014.0",
"wrangler": "^4.43.0"
}
}
替代方案(Playwright):
{
"dependencies": {
"@cloudflare/playwright": "^1.0.0"
}
}
故障排查
问题:"Cannot read properties of undefined (reading 'fetch')"
解决方案: 将浏览器绑定传递给 puppeteer.launch():
const browser = await puppeteer.launch(env.MYBROWSER); // Not just puppeteer.launch()
问题:XPath 选择器不起作用
解决方案: 使用 CSS 选择器,或通过 XPathEvaluator 调用 page.evaluate()(参见 Issue #1)
问题:浏览器在 60 秒后关闭
解决方案: 使用 keep_alive 延长超时时间:
const browser = await puppeteer.launch(env.MYBROWSER, { keep_alive: 300000 });
问题:达到速率限制
解决方案: 重用会话、使用标签页,并在启动前检查限制(参见 Issue #4)
问题:本地开发请求超过 1MB 时失败
解决方案: 在 wrangler.jsonc 中启用远程绑定:
{ "browser": { "binding": "MYBROWSER", "remote": true } }
问题:网站将其识别为机器人并进行拦截
解决方案: 无法绕过。如果这是你自己的区域,请创建 WAF 跳过规则(参见 Issue #6)
有疑问?遇到问题?
- 查看
references/common-errors.md,了解详细解决方案 - 查看
references/session-management.md,了解性能优化 - 确认已在 wrangler.jsonc 中配置浏览器绑定
- 查看官方文档:https://developers.cloudflare.com/browser-rendering/
- 确保已启用
nodejs_compat兼容性标志