cloudflare-browser-rendering

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cloudflare Browser Rendering - Complete Reference

Cloudflare 浏览器渲染 - 完整参考指南

Production-ready knowledge domain for building browser automation workflows with Cloudflare Browser Rendering.
Status: Production Ready ✅ Last Updated: 2026-01-21 Dependencies: cloudflare-worker-base (for Worker setup) Latest Versions: @cloudflare/puppeteer@1.0.4, @cloudflare/playwright@1.1.0, wrangler@4.59.3
Recent Updates (2025):
  • Sept 2025: Playwright v1.55 GA, Stagehand framework support (Workers AI), /links excludeExternalLinks param
  • Aug 2025: Billing GA (Aug 20), /sessions endpoint in local dev, X-Browser-Ms-Used header
  • July 2025: Playwright v1.54.1 + MCP v0.0.30, Playwright local dev support (wrangler@4.26.0+), Puppeteer v22.13.1 sync, /content returns title, /json custom_ai param, /screenshot viewport 1920x1080 default
  • June 2025: Web Bot Auth headers auto-included
  • April 2025: Playwright support launched, free tier introduced

用于在Cloudflare浏览器渲染服务上构建浏览器自动化工作流的生产级知识库。
状态:已就绪可用于生产 ✅ 最后更新:2026-01-21 依赖项:cloudflare-worker-base(用于Worker配置) 最新版本:@cloudflare/puppeteer@1.0.4、@cloudflare/playwright@1.1.0、wrangler@4.59.3
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(需wrangler@4.26.0+),同步Puppeteer v22.13.1版本,/content接口返回页面标题,/json接口新增custom_ai参数,/screenshot接口默认视口设为1920x1080
  • 2025年6月:自动包含Web Bot认证请求头
  • 2025年4月:推出Playwright支持,新增免费额度

Table of Contents

目录

Quick Start (5 minutes)

快速开始(5分钟)

1. Add Browser Binding

1. 添加浏览器绑定

wrangler.jsonc:
jsonc
{
  "name": "browser-worker",
  "main": "src/index.ts",
  "compatibility_date": "2023-03-14",
  "compatibility_flags": ["nodejs_compat"],
  "browser": {
    "binding": "MYBROWSER"
  }
}
Why nodejs_compat? Browser Rendering requires Node.js APIs and polyfills.
wrangler.jsonc:
jsonc
{
  "name": "browser-worker",
  "main": "src/index.ts",
  "compatibility_date": "2023-03-14",
  "compatibility_flags": ["nodejs_compat"],
  "browser": {
    "binding": "MYBROWSER"
  }
}
为什么需要nodejs_compat? 浏览器渲染需要Node.js API和polyfill支持。

2. Install Puppeteer

2. 安装Puppeteer

bash
npm install @cloudflare/puppeteer
bash
npm install @cloudflare/puppeteer

3. Take Your First Screenshot

3. 捕获第一张截图

typescript
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" }
    });
  }
};
typescript
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";

    // 启动浏览器
    const browser = await puppeteer.launch(env.MYBROWSER);
    const page = await browser.newPage();

    // 导航并捕获截图
    await page.goto(url);
    const screenshot = await page.screenshot();

    // 清理资源
    await browser.close();

    return new Response(screenshot, {
      headers: { "content-type": "image/png" }
    });
  }
};

4. Deploy

4. 部署

bash
npx wrangler deploy
Test at:
https://your-worker.workers.dev/?url=https://example.com
CRITICAL:
  • Always pass
    env.MYBROWSER
    to
    puppeteer.launch()
    (not undefined)
  • Always call
    browser.close()
    when done (or use
    browser.disconnect()
    for session reuse)
  • Use
    nodejs_compat
    compatibility flag

bash
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 Overview

浏览器渲染概述

What is Browser Rendering?

什么是浏览器渲染?

Cloudflare Browser Rendering provides headless Chromium browsers running on Cloudflare's global network. Use familiar tools like Puppeteer and Playwright to automate browser tasks:
  • Screenshots - Capture visual snapshots of web pages
  • PDF Generation - Convert HTML/URLs to PDFs
  • Web Scraping - Extract content from dynamic websites
  • Testing - Automate frontend tests
  • Crawling - Navigate multi-page workflows
Cloudflare浏览器渲染服务在Cloudflare全球网络上运行无头Chromium浏览器。你可以使用熟悉的Puppeteer和Playwright工具来自动化浏览器任务:
  • 截图 - 捕获网页的视觉快照
  • PDF生成 - 将HTML/URL转换为PDF
  • 网页爬取 - 从动态网站提取内容
  • 测试 - 自动化前端测试
  • 站点遍历 - 导航多页面工作流

Two Integration Methods

两种集成方式

MethodBest ForComplexity
Workers BindingsComplex automation, custom workflows, session managementAdvanced
REST APISimple screenshot/PDF tasksSimple
This skill covers Workers Bindings (the advanced method with full Puppeteer/Playwright APIs).
方式最佳适用场景复杂度
Workers绑定复杂自动化、自定义工作流、会话管理高级
REST API简单的截图/PDF任务简单
本指南聚焦Workers绑定(支持完整Puppeteer/Playwright API的高级方式)。

Puppeteer vs Playwright

Puppeteer vs Playwright

FeaturePuppeteerPlaywright
API FamiliarityMost popularGrowing adoption
Package
@cloudflare/puppeteer@1.0.4
@cloudflare/playwright@1.0.0
Session Management✅ Advanced APIs⚠️ Basic
Browser SupportChromium onlyChromium only (Firefox/Safari not yet supported)
Best ForScreenshots, PDFs, scrapingTesting, frontend automation
Recommendation: Use Puppeteer for most use cases. Playwright is ideal if you're already using it for testing.

特性PuppeteerPlaywright
API熟悉度最受欢迎使用率持续增长
包名
@cloudflare/puppeteer@1.0.4
@cloudflare/playwright@1.0.0
会话管理✅ 高级API⚠️ 基础功能
浏览器支持仅Chromium仅Chromium(暂不支持Firefox/Safari)
最佳适用场景截图、PDF生成、网页爬取测试、前端自动化
推荐:大多数场景使用Puppeteer。如果你已在使用Playwright进行测试,那么它是理想选择。

Puppeteer API Reference

Puppeteer API参考

Core APIs (complete reference: https://pptr.dev/api/):
Global Functions:
  • puppeteer.launch(env.MYBROWSER, options?)
    - Launch new browser (CRITICAL: must pass binding)
  • puppeteer.connect(env.MYBROWSER, sessionId)
    - Connect to existing session
  • puppeteer.sessions(env.MYBROWSER)
    - List running sessions
  • puppeteer.history(env.MYBROWSER)
    - List recent sessions (open + closed)
  • puppeteer.limits(env.MYBROWSER)
    - Check account limits
Browser Methods:
  • browser.newPage()
    - Create new tab (preferred over launching new browsers)
  • browser.sessionId()
    - Get session ID for reuse
  • browser.close()
    - Terminate session
  • browser.disconnect()
    - Keep session alive for reuse
  • browser.createBrowserContext()
    - Isolated incognito context (separate cookies/cache)
Page Methods:
  • page.goto(url, { waitUntil, timeout })
    - Navigate (use
    "networkidle0"
    for dynamic content)
  • page.screenshot({ fullPage, type, quality, clip })
    - Capture image
  • page.pdf({ format, printBackground, margin })
    - Generate PDF
  • page.evaluate(() => ...)
    - Execute JS in browser (data extraction, XPath workaround)
  • page.content()
    /
    page.setContent(html)
    - Get/set HTML
  • page.waitForSelector(selector)
    - Wait for element
  • page.type(selector, text)
    /
    page.click(selector)
    - Form interaction
Critical Patterns:
typescript
// 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;
});

核心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)
    - 表单交互
关键模式:
typescript
// 必须传递绑定
const browser = await puppeteer.launch(env.MYBROWSER); // ✅
// const browser = await puppeteer.launch(); // ❌ 错误!

// 会话复用提升性能
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);
}

// 保持会话活跃
await browser.disconnect(); // 不要关闭

// XPath替代方案(不直接支持XPath)
const data = await page.evaluate(() => {
  return new XPathEvaluator()
    .createExpression("/html/body/div/h1")
    .evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE)
    .singleNodeValue.innerHTML;
});

Playwright API Reference

Playwright API参考

Status: GA (Sept 2025) - Playwright v1.55, MCP v0.0.30 support, local dev support (wrangler@4.26.0+)
Installation:
bash
npm install @cloudflare/playwright
Configuration Requirements (2025 Update):
jsonc
{
  "compatibility_flags": ["nodejs_compat"],
  "compatibility_date": "2025-09-15"  // Required for Playwright v1.55
}
Basic Usage:
typescript
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 vs Playwright:
  • Import:
    puppeteer
    vs
    { chromium }
    from "@cloudflare/playwright"
  • Session API: Puppeteer has advanced session management (sessions/history/limits), Playwright basic
  • Auto-waiting: Playwright has built-in auto-waiting, Puppeteer requires manual
    waitForSelector()
  • MCP Support: Playwright MCP v0.0.30 (July 2025), Playwright MCP server available
  • Latest Version: Playwright v1.57 support (Jan 2026 update)
Recommendation: Use Puppeteer for session reuse patterns. Use Playwright if migrating existing tests or need MCP integration.

状态:正式发布(2025年9月)- 支持Playwright v1.55、MCP v0.0.30,本地开发支持(需wrangler@4.26.0+)
安装:
bash
npm install @cloudflare/playwright
配置要求(2025年更新):
jsonc
{
  "compatibility_flags": ["nodejs_compat"],
  "compatibility_date": "2025-09-15"  // Playwright v1.55必需
}
基础用法:
typescript
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 vs Playwright对比:
  • 导入方式
    puppeteer
    vs 从"@cloudflare/playwright"导入
    { chromium }
  • 会话API:Puppeteer拥有高级会话管理(sessions/history/limits),Playwright仅支持基础功能
  • 自动等待:Playwright内置自动等待,Puppeteer需要手动调用
    waitForSelector()
  • MCP支持:Playwright支持MCP v0.0.30(2025年7月),提供Playwright MCP服务器
  • 最新版本:支持Playwright v1.57(2026年1月更新)
推荐:会话复用场景使用Puppeteer。如果迁移现有测试或需要MCP集成,使用Playwright。

Session Management

会话管理

Why: Launching new browsers is slow and consumes concurrency limits. Reuse sessions for faster response, lower concurrency usage, better resource utilization.
原因:启动新浏览器速度慢且会消耗并发限制。复用会话可提升响应速度、降低并发占用、优化资源利用率。

Session Reuse Pattern (Critical)

会话复用模式(关键)

typescript
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;
    }
  }
};
Key Rules:
  • browser.disconnect()
    - Keep session alive for reuse
  • browser.close()
    - Only on errors or when truly done
  • ✅ Always handle connection failures
typescript
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("连接失败,启动新浏览器");
    }
  }

  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(); // ✅ 保持会话活跃以便复用

      return new Response(screenshot, {
        headers: { "content-type": "image/png" }
      });
    } catch (error) {
      await browser.close(); // ❌ 出错时关闭会话
      throw error;
    }
  }
};
核心规则:
  • browser.disconnect()
    - 保持会话活跃以便复用
  • browser.close()
    - 仅在出错或确实不再使用时调用
  • ✅ 始终处理连接失败情况

Browser Contexts (Cookie/Cache Isolation)

浏览器上下文(Cookie/缓存隔离)

Use
browser.createBrowserContext()
to share browser but isolate cookies/cache:
typescript
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
使用
browser.createBrowserContext()
共享浏览器实例但隔离Cookie/缓存:
typescript
const browser = await puppeteer.launch(env.MYBROWSER);
const context1 = await browser.createBrowserContext(); // 用户1
const context2 = await browser.createBrowserContext(); // 用户2

const page1 = await context1.newPage();
const page2 = await context2.newPage();
// 每个上下文拥有独立的Cookie/缓存

Multiple Tabs Pattern

多标签页模式

❌ Bad: Launch 10 browsers for 10 URLs (wastes concurrency) ✅ Good: 1 browser, 10 tabs via
Promise.all()
+
browser.newPage()
typescript
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();

❌ 不良实践:为10个URL启动10个浏览器(浪费并发资源) ✅ 最佳实践:1个浏览器,通过
Promise.all()
+
browser.newPage()
创建10个标签页
typescript
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();

Common Patterns

常见模式

Screenshot with KV Caching

结合KV缓存的截图

Cache screenshots to reduce browser usage and improve performance:
typescript
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" } });
  }
};
缓存截图以减少浏览器使用并提升性能:
typescript
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("缺少?url参数", { status: 400 });

    const normalizedUrl = new URL(url).toString();

    // 先检查缓存
    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();

      // 缓存24小时
      await env.CACHE.put(normalizedUrl, screenshot, { expirationTtl: 60 * 60 * 24 });
    }

    return new Response(screenshot, { headers: { "content-type": "image/png" } });
  }
};

AI-Enhanced Scraping

AI增强型爬取

Combine Browser Rendering with Workers AI for structured data extraction:
typescript
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) });
  }
};
Other Common Patterns: PDF generation (
page.pdf()
), structured scraping (
page.evaluate()
), form automation (
page.type()
+
page.click()
). See bundled
templates/
directory.

结合浏览器渲染与Workers AI实现结构化数据提取:
typescript
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");

    // 爬取页面内容
    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();

    // 使用AI提取结构化数据
    const response = await env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
      messages: [{
        role: "user",
        content: `从以下HTML中提取产品信息并以JSON格式返回。需包含:名称、价格、描述。\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/
目录。

Pricing & Limits

定价与限制

Billing GA: August 20, 2025
Free Tier: 10 min/day, 3 concurrent, 3 launches/min, 60s timeout Paid Tier: 10 hrs/month included ($0.09/hr after), 30 concurrent ($2.00/browser after), 30 launches/min, 60s-10min timeout
Concurrency Calculation: Monthly average of daily peak usage (e.g., 35 browsers avg = (35 - 30 included) × $2.00 = $10.00/mo)
Rate Limiting: Enforced with a fixed per-second fill rate (NOT burst-friendly). 30 req/min = 1 req every 2 seconds. You CANNOT send all 30 requests at once, even if quota is unused. Check
puppeteer.limits(env.MYBROWSER)
before launching:
typescript
const limits = await puppeteer.limits(env.MYBROWSER);
if (limits.allowedBrowserAcquisitions === 0) {
  const delay = limits.timeUntilNextAllowedBrowserAcquisition || 1000;
  await new Promise(resolve => setTimeout(resolve, delay));
}

计费正式启动:2025年8月20日
免费额度:每日10分钟,3个并发,每分钟3次启动,60秒超时 付费额度:每月包含10小时(超出后$0.09/小时),30个并发(超出后$2.00/浏览器/月),每分钟30次启动,超时时间60秒-10分钟
并发计算方式:每日峰值使用量的月平均值(例如:平均35个浏览器 = (35 - 30) × $2.00 = $10.00/月)
速率限制:采用固定每秒填充速率(不支持突发流量)。30次请求/分钟 = 每2秒1次请求。即使配额未用完,也不能一次性发送30个请求。启动前请检查
puppeteer.limits(env.MYBROWSER)
typescript
const limits = await puppeteer.limits(env.MYBROWSER);
if (limits.allowedBrowserAcquisitions === 0) {
  const delay = limits.timeUntilNextAllowedBrowserAcquisition || 1000;
  await new Promise(resolve => setTimeout(resolve, delay));
}

Known Issues Prevention

已知问题预防

This skill prevents 8 documented issues:

本指南可预防8个已记录的问题

Issue #1: XPath Selectors Not Supported

问题#1:XPath选择器不被支持

Error: "XPath selector not supported" or selector failures Source: https://developers.cloudflare.com/browser-rendering/faq/#why-cant-i-use-an-xpath-selector-when-using-browser-rendering-with-puppeteer Why It Happens: XPath poses a security risk to Workers Prevention: Use CSS selectors or
page.evaluate()
with XPathEvaluator
Solution:
typescript
// ❌ 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;
});

错误信息: "XPath selector not supported" 或选择器执行失败 来源: https://developers.cloudflare.com/browser-rendering/faq/#why-cant-i-use-an-xpath-selector-when-using-browser-rendering-with-puppeteer 原因: XPath对Workers存在安全风险 预防方案: 使用CSS选择器或在
page.evaluate()
中使用XPathEvaluator
解决方案:
typescript
// ❌ 不要直接使用XPath(不支持)
// await page.$x('/html/body/div/h1')

// ✅ 使用CSS选择器
const heading = await page.$("div > h1");

// ✅ 或在page.evaluate()中使用XPath
const innerHtml = await page.evaluate(() => {
  return new XPathEvaluator()
    .createExpression("/html/body/div/h1")
    .evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE)
    .singleNodeValue.innerHTML;
});

Issue #2: Browser Binding Not Passed (Fetcher Type Confusion)

问题#2:未传递浏览器绑定(Fetcher类型混淆)

Error: "Cannot read properties of undefined (reading 'fetch')" or "RPC receiver does not implement the method 'launch'" Source: GitHub Issue #10772, https://developers.cloudflare.com/browser-rendering/faq/#cannot-read-properties-of-undefined-reading-fetch Why It Happens:
puppeteer.launch()
called without browser binding, or trying to call
env.MYBROWSER.launch()
directly. The browser binding is a Fetcher (REST API wrapper), not a browser instance. Prevention: Always pass
env.MYBROWSER
to
puppeteer.launch()
or
chromium.launch()
wrapper
Solution:
typescript
// ❌ Missing browser binding
const browser = await puppeteer.launch(); // Error!

// ❌ Wrong - trying to call launch() on Fetcher directly
const browser = await env.MYBROWSER.launch(); // "RPC receiver does not implement the method 'launch'"

// ✅ Pass binding to Puppeteer/Playwright wrapper
const browser = await puppeteer.launch(env.MYBROWSER);
// or for Playwright:
const browser = await chromium.launch(env.MYBROWSER);
TypeScript Type Explanation:
typescript
interface Env {
  MYBROWSER: Fetcher; // It's a Fetcher, not a Browser!
}

错误信息: "Cannot read properties of undefined (reading 'fetch')" 或 "RPC receiver does not implement the method 'launch'" 来源: GitHub Issue #10772, https://developers.cloudflare.com/browser-rendering/faq/#cannot-read-properties-of-undefined-reading-fetch 原因: 调用
puppeteer.launch()
时未传入浏览器绑定,或尝试直接调用
env.MYBROWSER.launch()
。浏览器绑定是Fetcher(REST API包装器),而非浏览器实例。 预防方案: 始终将
env.MYBROWSER
传递给
puppeteer.launch()
chromium.launch()
包装函数
解决方案:
typescript
// ❌ 缺少浏览器绑定
const browser = await puppeteer.launch(); // 错误!

// ❌ 错误用法 - 尝试直接在Fetcher上调用launch()
const browser = await env.MYBROWSER.launch(); // "RPC receiver does not implement the method 'launch'"

// ✅ 将绑定传递给Puppeteer/Playwright包装函数
const browser = await puppeteer.launch(env.MYBROWSER);
// 或Playwright用法:
const browser = await chromium.launch(env.MYBROWSER);
TypeScript类型说明:
typescript
interface Env {
  MYBROWSER: Fetcher; // 这是Fetcher,不是Browser实例!
}

Issue #3: Browser Timeout (60 seconds)

问题#3:浏览器超时(60秒)

Error: Browser closes unexpectedly after 60 seconds Source: https://developers.cloudflare.com/browser-rendering/platform/limits/#note-on-browser-timeout Why It Happens: Default timeout is 60 seconds of inactivity Prevention: Use
keep_alive
option to extend up to 10 minutes
Solution:
typescript
// Extend timeout to 5 minutes for long-running tasks
const browser = await puppeteer.launch(env.MYBROWSER, {
  keep_alive: 300000 // 5 minutes = 300,000 ms
});
Note: Browser closes if no devtools commands for the specified duration.

错误现象: 浏览器在60秒后意外关闭 来源: https://developers.cloudflare.com/browser-rendering/platform/limits/#note-on-browser-timeout 原因: 默认超时为60秒无活动时间 预防方案: 使用
keep_alive
选项将超时延长至10分钟
解决方案:
typescript
// 为长时间运行的任务将超时延长至5分钟
const browser = await puppeteer.launch(env.MYBROWSER, {
  keep_alive: 300000 // 5分钟 = 300,000毫秒
});
注意: 如果在指定时长内没有devtools命令,浏览器将关闭。

Issue #4: Concurrency Limits Reached

问题#4:达到并发限制

Error: "Rate limit exceeded" or new browser launch fails Source: https://developers.cloudflare.com/browser-rendering/platform/limits/, Changelog 2025-09-25 Why It Happens: Exceeded concurrent browser limit (3 free, 30 paid as of Sept 2025) Prevention: Reuse sessions, use tabs instead of multiple browsers, check limits before launching, throttle requests to per-second rate
Solutions:
typescript
// 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

错误信息: "Rate limit exceeded" 或新浏览器启动失败 来源: https://developers.cloudflare.com/browser-rendering/platform/limits/, Changelog 2025-09-25 原因: 超出浏览器并发限制(截至2025年9月,免费版3个,付费版30个) 预防方案: 复用会话、使用标签页而非多个浏览器、启动前检查限制、按每秒速率节流请求
解决方案:
typescript
// 1. 启动前检查限制
const limits = await puppeteer.limits(env.MYBROWSER);
if (limits.allowedBrowserAcquisitions === 0) {
  return new Response("达到并发限制", { status: 429 });
}

// 2. 复用会话
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. 使用标签页而非多个浏览器
const browser = await puppeteer.launch(env.MYBROWSER);
const page1 = await browser.newPage();
const page2 = await browser.newPage(); // 同一浏览器,不同标签页

Issue #5: Local Development Request Size Limit

问题#5:本地开发请求大小限制

Error: Request larger than 1MB fails in
wrangler dev
Source: https://developers.cloudflare.com/browser-rendering/faq/#does-local-development-support-all-browser-rendering-features Why It Happens: Local development limitation Prevention: Use
remote: true
in browser binding for local dev
Solution:
jsonc
// wrangler.jsonc for local development
{
  "browser": {
    "binding": "MYBROWSER",
    "remote": true  // Use real headless browser during dev
  }
}

错误现象:
wrangler dev
中,大于1MB的请求失败 来源: https://developers.cloudflare.com/browser-rendering/faq/#does-local-development-support-all-browser-rendering-features 原因: 本地开发的限制 预防方案: 在浏览器绑定中设置
remote: true
用于本地开发
解决方案:
jsonc
// 本地开发用wrangler.jsonc
{
  "browser": {
    "binding": "MYBROWSER",
    "remote": true  // 开发时使用真实无头浏览器
  }
}

Issue #6: Bot Protection Always Triggered

问题#6:始终触发机器人防护

Error: Website blocks requests as bot traffic Source: https://developers.cloudflare.com/browser-rendering/faq/#will-browser-rendering-bypass-cloudflares-bot-protection Why It Happens: Browser Rendering requests always identified as bots Prevention: Cannot bypass; if scraping your own zone, create WAF skip rule (requires Enterprise plan for Bot Management)
Solution:
typescript
// ❌ Cannot bypass bot protection
// Requests will always be identified as bots

// ✅ If scraping your own Cloudflare zone (Enterprise plan only):
// 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

await page.setExtraHTTPHeaders({
  'X-Custom-Auth': 'your-secret-token'
});

// Note: Automatic headers are included:
// - cf-biso-request-id
// - cf-biso-devtools
Important: Free/Pro/Business plans CANNOT bypass bot detection even on their own sites. Enterprise plan with Bot Management is required for WAF allowlisting.

错误现象: 网站将请求识别为机器人流量并阻止 来源: https://developers.cloudflare.com/browser-rendering/faq/#will-browser-rendering-bypass-cloudflares-bot-protection 原因: 浏览器渲染请求始终会被识别为机器人 预防方案: 无法绕过;如果爬取自己的站点,可创建WAF跳过规则(需要企业版Bot Management)
解决方案:
typescript
// ❌ 无法绕过机器人防护
// 请求始终会被识别为机器人

// ✅ 如果爬取自己的Cloudflare站点(仅企业版支持):
// 1. 进入Security > WAF > Custom rules
// 2. 创建跳过规则,使用自定义请求头:
//    请求头:X-Custom-Auth
//    值:your-secret-token
// 3. 在爬取请求中传递该请求头

await page.setExtraHTTPHeaders({
  'X-Custom-Auth': 'your-secret-token'
});

// 注意:会自动包含以下请求头:
// - cf-biso-request-id
// - cf-biso-devtools
重要提示: 免费版/专业版/商业版即使爬取自己的站点也无法绕过机器人检测。需要企业版Bot Management才能配置WAF允许规则。

Issue #7: page.evaluate() Function Name Injection (__name Error)

问题#7:page.evaluate()函数名注入(__name错误)

Error:
ReferenceError: __name is not defined
Source: GitHub Issue #7107 Why It Happens: esbuild minification (wrangler 3.80.1+) injects
__name()
helper calls in arrow functions with nested function declarations. These run in browser context where the helper doesn't exist. Prevention: Keep
page.evaluate()
functions simple - avoid nested function declarations Applies to: wrangler 3.80.1 - 3.83.0 (fixed in 3.83.0+)
Solution:
typescript
// ❌ Avoid nested function declarations
const data = await page.evaluate(async () => {
  function toNumber(str: string | undefined): number | undefined {
    const num = typeof str === 'string' ? str.replaceAll('.', '').replaceAll(',', '.').match(/[+-]?([0-9]*[.])?[0-9]+/) : false;
    if (num) {
      return Number(num[0]);
    } else {
      return undefined;
    }
  }
  return toNumber('123.456');
});
// Error: ReferenceError: __name is not defined

// ✅ Inline the logic without nested functions
const data = await page.evaluate(async () => {
  const str = '123.456';
  const num = typeof str === 'string' ? str.replaceAll('.', '').replaceAll(',', '.').match(/[+-]?([0-9]*[.])?[0-9]+/) : false;
  return num ? Number(num[0]) : undefined;
});

// ✅ Or update to wrangler 3.83.0+
// npm install wrangler@latest
Note: This also affects
page.waitForSelector()
with complex callbacks. Fixed in wrangler 3.83.0+ (Nov 2024).

错误信息:
ReferenceError: __name is not defined
来源: GitHub Issue #7107 原因: esbuild压缩(wrangler 3.80.1+)会在包含嵌套函数声明的箭头函数中注入
__name()
辅助调用。这些调用在浏览器上下文中运行时,该辅助函数不存在。 预防方案: 保持
page.evaluate()
函数简洁 - 避免嵌套函数声明 影响版本: wrangler 3.80.1 - 3.83.0(3.83.0+已修复)
解决方案:
typescript
// ❌ 避免嵌套函数声明
const data = await page.evaluate(async () => {
  function toNumber(str: string | undefined): number | undefined {
    const num = typeof str === 'string' ? str.replaceAll('.', '').replaceAll(',', '.').match(/[+-]?([0-9]*[.])?[0-9]+/) : false;
    if (num) {
      return Number(num[0]);
    } else {
      return undefined;
    }
  }
  return toNumber('123.456');
});
// 错误:ReferenceError: __name is not defined

// ✅ 内联逻辑,不使用嵌套函数
const data = await page.evaluate(async () => {
  const str = '123.456';
  const num = typeof str === 'string' ? str.replaceAll('.', '').replaceAll(',', '.').match(/[+-]?([0-9]*[.])?[0-9]+/) : false;
  return num ? Number(num[0]) : undefined;
});

// ✅ 或升级到wrangler 3.83.0+
// npm install wrangler@latest
注意: 这也会影响带有复杂回调的
page.waitForSelector()
。该问题已在wrangler 3.83.0+(2024年11月)中修复。

Issue #8: waitForSelector() Timeout Behavior Changed

问题#8:waitForSelector()超时行为变更

Error: Code that relied on indefinite waiting now times out Source: Changelog 2026-01-07 Why It Happens:
waitForSelector()
previously did NOT timeout when selectors weren't found (hung indefinitely). This was fixed to properly honor timeout values. Prevention: Always set explicit timeouts and handle timeout errors Applies to: All code written before Jan 2026 that relied on indefinite waiting
Solution:
typescript
// ❌ Old behavior - would hang forever if selector not found
await page.waitForSelector('#dynamic-element');

// ✅ New behavior - properly times out (set explicit timeout)
try {
  await page.waitForSelector('#dynamic-element', { timeout: 5000 });
} catch (error) {
  if (error.name === 'TimeoutError') {
    console.log('Element not found within 5 seconds');
    // Handle missing element gracefully
  } else {
    throw error;
  }
}

// ✅ Use longer timeout for slow-loading elements
await page.waitForSelector('#slow-element', { timeout: 30000 }); // 30 seconds
Note: This is a breaking fix (behavior change). Code that relied on indefinite waiting will now timeout and throw errors. Always handle
TimeoutError
gracefully.

错误现象: 依赖无限等待的代码现在会超时 来源: Changelog 2026-01-07 原因: 之前
waitForSelector()
在未找到选择器时不会超时(无限挂起)。现在已修复为正确遵守超时设置。 预防方案: 始终设置显式超时并处理超时错误 影响范围: 2026年1月前编写的依赖无限等待的代码
解决方案:
typescript
// ❌ 旧行为 - 如果未找到元素会无限挂起
await page.waitForSelector('#dynamic-element');

// ✅ 新行为 - 正确超时(设置显式超时)
try {
  await page.waitForSelector('#dynamic-element', { timeout: 5000 });
} catch (error) {
  if (error.name === 'TimeoutError') {
    console.log('5秒内未找到元素');
    // 优雅处理元素缺失情况
  } else {
    throw error;
  }
}

// ✅ 为加载缓慢的元素设置更长超时
await page.waitForSelector('#slow-element', { timeout: 30000 }); // 30秒
注意: 这是一个破坏性修复(行为变更)。依赖无限等待的代码现在会超时并抛出错误。请始终优雅处理
TimeoutError

Production Checklist

生产环境检查清单

Before deploying Browser Rendering Workers to production:
在将浏览器渲染Workers部署到生产环境前,请完成以下检查:

Configuration

配置

  • Browser binding configured in wrangler.jsonc
  • nodejs_compat flag enabled (required for Browser Rendering)
  • Keep-alive timeout set if tasks take > 60 seconds
  • Remote binding enabled for local development if needed
  • 已配置浏览器绑定 在wrangler.jsonc中
  • 已启用nodejs_compat标志(浏览器渲染必需)
  • 已设置保持活跃超时 如果任务耗时超过60秒
  • 已启用远程绑定 如需本地开发使用

Error Handling

错误处理

  • Retry logic implemented for rate limits
  • Timeout handling for page.goto()
  • Browser cleanup in try-finally blocks
  • Concurrency limit checks before launching browsers
  • Graceful degradation when browser unavailable
  • 已实现重试逻辑 针对速率限制
  • 已处理超时 针对page.goto()
  • 已在try-finally块中清理浏览器资源
  • 已在启动浏览器前检查并发限制
  • 已实现优雅降级 当浏览器不可用时

Performance

性能

  • Session reuse implemented for high-traffic routes
  • Multiple tabs used instead of multiple browsers
  • Incognito contexts for session isolation
  • KV caching for repeated screenshots/PDFs
  • Batch operations to maximize browser utilization
  • 已实现会话复用 针对高流量路由
  • 已使用多标签页 而非多个浏览器
  • 已使用隐身上下文 用于会话隔离
  • 已使用KV缓存 针对重复的截图/PDF
  • 已实现批量操作 最大化浏览器利用率

Monitoring

监控

Security

安全

  • Input validation for URLs (prevent SSRF)
  • Timeout limits to prevent abuse
  • Rate limiting on public endpoints
  • Authentication for sensitive scraping endpoints
  • WAF rules if scraping your own zone
  • 已验证URL输入 防止SSRF攻击
  • 已设置超时限制 防止滥用
  • 已在公共端点设置速率限制
  • 已为敏感爬取端点添加认证
  • 已配置WAF规则 如果爬取自己的站点

Testing

测试

  • Test screenshot capture with various page sizes
  • Test PDF generation with custom HTML
  • Test scraping with dynamic content (networkidle0)
  • Test error scenarios (invalid URLs, timeouts)
  • Load test concurrency limits

  • 已测试不同页面尺寸的截图捕获
  • 已测试自定义HTML的PDF生成
  • 已测试动态内容爬取(使用networkidle0)
  • 已测试错误场景(无效URL、超时)
  • 已进行负载测试 验证并发限制

Error Handling Best Practices

错误处理最佳实践

Production Pattern - Use try-catch with proper cleanup:
typescript
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;
  }
}
Key Principles: Check limits → Reuse sessions → Execute → Disconnect on success, close on error

生产环境模式 - 使用try-catch并正确清理资源:
typescript
async function withBrowser<T>(env: Env, fn: (browser: Browser) => Promise<T>): Promise<T> {
  let browser: Browser | null = null;

  try {
    // 1. 启动前检查限制
    const limits = await puppeteer.limits(env.MYBROWSER);
    if (limits.allowedBrowserAcquisitions === 0) {
      throw new Error("达到速率限制");
    }

    // 2. 优先尝试会话复用
    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. 执行用户函数
    const result = await fn(browser);

    // 4. 断开连接(保持会话活跃)
    await browser.disconnect();
    return result;
  } catch (error) {
    // 5. 出错时关闭会话
    if (browser) await browser.close();
    throw error;
  }
}
核心原则:检查限制 → 复用会话 → 执行操作 → 成功时断开连接,出错时关闭会话

Using Bundled Resources

使用附带资源

Templates (templates/)

模板(templates/)

Ready-to-use code templates for common patterns:
  • basic-screenshot.ts
    - Minimal screenshot example
  • screenshot-with-kv-cache.ts
    - Screenshot with KV caching
  • pdf-generation.ts
    - Generate PDFs from HTML or URLs
  • web-scraper-basic.ts
    - Basic web scraping pattern
  • web-scraper-batch.ts
    - Batch scrape multiple URLs
  • session-reuse.ts
    - Session reuse for performance
  • ai-enhanced-scraper.ts
    - Scraping with Workers AI
  • playwright-example.ts
    - Playwright alternative example
  • wrangler-browser-config.jsonc
    - Browser binding configuration
Usage:
bash
undefined
针对常见模式的即用型代码模板:
  • 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
    - 浏览器绑定配置
使用方法:
bash
undefined

Copy template to your project

复制模板到你的项目

cp ~/.claude/skills/cloudflare-browser-rendering/templates/basic-screenshot.ts src/index.ts
undefined
cp ~/.claude/skills/cloudflare-browser-rendering/templates/basic-screenshot.ts src/index.ts
undefined

References (references/)

参考文档(references/)

Deep-dive documentation:
  • session-management.md
    - Complete session reuse guide
  • pricing-and-limits.md
    - Detailed pricing breakdown
  • common-errors.md
    - All known issues and solutions
  • puppeteer-vs-playwright.md
    - Feature comparison and migration
When to load: Reference when implementing advanced patterns or debugging specific issues.

深度文档:
  • session-management.md
    - 完整会话复用指南
  • pricing-and-limits.md
    - 详细定价说明
  • common-errors.md
    - 所有已知问题及解决方案
  • puppeteer-vs-playwright.md
    - 特性对比与迁移指南
加载时机: 实现高级模式或排查特定问题时参考。

Dependencies

依赖项

Required:
  • @cloudflare/puppeteer@1.0.4
    - Puppeteer for Workers
  • wrangler@4.43.0+
    - Cloudflare CLI
Optional:
  • @cloudflare/playwright@1.0.0
    - Playwright for Workers (alternative)
  • @cloudflare/workers-types@4.20251014.0+
    - TypeScript types
Related Skills:
  • cloudflare-worker-base
    - Worker setup with Hono
  • cloudflare-kv
    - KV caching for screenshots
  • cloudflare-r2
    - R2 storage for generated files
  • cloudflare-workers-ai
    - AI-enhanced scraping

必需:
  • @cloudflare/puppeteer@1.0.4
    - 适用于Workers的Puppeteer
  • wrangler@4.43.0+
    - Cloudflare命令行工具
可选:
  • @cloudflare/playwright@1.0.0
    - 适用于Workers的Playwright(替代方案)
  • @cloudflare/workers-types@4.20251014.0+
    - TypeScript类型定义
相关技能:
  • cloudflare-worker-base
    - 使用Hono的Worker配置
  • cloudflare-kv
    - 截图的KV缓存
  • cloudflare-r2
    - 生成文件的R2存储
  • cloudflare-workers-ai
    - AI增强型爬取

Official Documentation

官方文档

Package Versions (Verified 2026-01-21)

已验证的包版本(2026-01-21)

json
{
  "dependencies": {
    "@cloudflare/puppeteer": "^1.0.4"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20251014.0",
    "wrangler": "^4.59.3"
  }
}
Alternative (Playwright):
json
{
  "dependencies": {
    "@cloudflare/playwright": "^1.1.0"
  }
}
Note: Playwright v1.1.0 includes support for Playwright v1.57 (Jan 2026). Wrangler 3.83.0+ fixes the
page.evaluate()
__name injection bug.

json
{
  "dependencies": {
    "@cloudflare/puppeteer": "^1.0.4"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20251014.0",
    "wrangler": "^4.59.3"
  }
}
替代方案(Playwright):
json
{
  "dependencies": {
    "@cloudflare/playwright": "^1.1.0"
  }
}
注意: Playwright v1.1.0包含对Playwright v1.57的支持(2026年1月更新)。Wrangler 3.83.0+修复了
page.evaluate()
的__name注入问题。

Troubleshooting

故障排除

Problem: "Cannot read properties of undefined (reading 'fetch')"

问题:"Cannot read properties of undefined (reading 'fetch')"

Solution: Pass browser binding to puppeteer.launch():
typescript
const browser = await puppeteer.launch(env.MYBROWSER); // Not just puppeteer.launch()
解决方案: 将浏览器绑定传递给puppeteer.launch():
typescript
const browser = await puppeteer.launch(env.MYBROWSER); // 不要只调用puppeteer.launch()

Problem: XPath selectors not working

问题:XPath选择器无法工作

Solution: Use CSS selectors or page.evaluate() with XPathEvaluator (see Issue #1)
解决方案: 使用CSS选择器或在page.evaluate()中使用XPathEvaluator(详见问题#1)

Problem: Browser closes after 60 seconds

问题:浏览器60秒后关闭

Solution: Extend timeout with keep_alive:
typescript
const browser = await puppeteer.launch(env.MYBROWSER, { keep_alive: 300000 });
解决方案: 使用keep_alive延长超时:
typescript
const browser = await puppeteer.launch(env.MYBROWSER, { keep_alive: 300000 });

Problem: Rate limit reached

问题:达到速率限制

Solution: Reuse sessions, use tabs, check limits before launching (see Issue #4)
解决方案: 复用会话、使用标签页、启动前检查限制(详见问题#4)

Problem: Local dev request > 1MB fails

问题:本地开发中大于1MB的请求失败

Solution: Enable remote binding in wrangler.jsonc:
jsonc
{ "browser": { "binding": "MYBROWSER", "remote": true } }
解决方案: 在wrangler.jsonc中启用远程绑定:
jsonc
{ "browser": { "binding": "MYBROWSER", "remote": true } }

Problem: Website blocks as bot

问题:网站将请求识别为机器人并阻止

Solution: Cannot bypass. If your own zone, create WAF skip rule (see Issue #6)

Questions? Issues?
  1. Check
    references/common-errors.md
    for detailed solutions
  2. Review
    references/session-management.md
    for performance optimization
  3. Verify browser binding is configured in wrangler.jsonc
  4. Check official docs: https://developers.cloudflare.com/browser-rendering/
  5. Ensure
    nodejs_compat
    compatibility flag is enabled
解决方案: 无法绕过。如果是自己的站点,创建WAF跳过规则(详见问题#6)

有疑问?遇到问题?
  1. 查看
    references/common-errors.md
    获取详细解决方案
  2. 查看
    references/session-management.md
    获取性能优化建议
  3. 验证wrangler.jsonc中是否已配置浏览器绑定
  4. 查看官方文档:https://developers.cloudflare.com/browser-rendering/
  5. 确保已启用
    nodejs_compat
    兼容性标志