mcpField
C
cloudflare-browser-rendering

cloudflare-browser-rendering

github.com/ovachiever云与 DevOps5663 次安装48 周前更新
代码仓库

关于 cloudflare-browser-rendering

cloudflare-browser-rendering 是使用 Cloudflare Browser Rendering 构建浏览器自动化工作流的参考技能,涵盖 Puppeteer、Playwright、会话管理及截图、PDF、抓取和测试模式。

主要功能

  1. 01Puppeteer 与 Playwright 参考
  2. 02浏览器会话管理
  3. 03截图与 PDF 工作流
  4. 04动态网站数据提取
  5. 05表单与前端测试自动化
  6. 06Cloudflare Workers 配置指南

适用场景

  1. 01捕获网页截图
  2. 02将网页生成 PDF
  3. 03提取动态网站数据
  4. 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.json
references/
common-errors.md
pricing-and-limits.md
puppeteer-vs-playwright.md
session-management.md
scripts/
check-versions.sh
templates/
ai-enhanced-scraper.ts
basic-screenshot.ts
pdf-generation.ts
playwright-example.ts
screenshot-with-kv-cache.ts
session-reuse.ts
web-scraper-basic.ts
web-scraper-batch.ts
wrangler-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 支持,引入免费套餐

目录

  1. 快速入门(5 分钟)
  2. Browser Rendering 概览
  3. Puppeteer API 参考
  4. Playwright API 参考
  5. 会话管理
  6. 常见模式
  7. 定价与限制
  8. 预防已知问题
  9. 生产环境检查清单

快速入门(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
特性PuppeteerPlaywright
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 }) - 生成 PDF
  • page.evaluate(() => ...) - 在浏览器中执行 JS(提取数据、XPath 变通方案)
  • page.content() / page.setContent(html) - 获取/设置 HTML
  • page.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
  • 使用批量操作最大限度地提高浏览器利用率
监控
安全性
  • 输入验证:验证 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 生成 PDF
  • web-scraper-basic.ts - 基本网页抓取模式
  • web-scraper-batch.ts - 批量抓取多个 URL
  • session-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-worker-base - 使用 Hono 配置 Worker
  • cloudflare-kv - 用于截图的 KV 缓存
  • cloudflare-r2 - 用于存储生成文件的 R2
  • cloudflare-workers-ai - AI 增强的抓取

官方文档


软件包版本(已于 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)


有疑问?遇到问题?

  1. 查看 references/common-errors.md,了解详细解决方案
  2. 查看 references/session-management.md,了解性能优化
  3. 确认已在 wrangler.jsonc 中配置浏览器绑定
  4. 查看官方文档:https://developers.cloudflare.com/browser-rendering/
  5. 确保已启用 nodejs_compat 兼容性标志

评论

支持 Markdown