cloudflare-browser-rendering — quality + safety report

In the Skillier index (secondsky__cloudflare-browser-rendering) · scanned 2026-06-03 · engine: builtin+triage

A
Quality
90/100
Safety

2 heuristic flags to review

Heuristic flags from the builtin scanner, which is known to over-flag (it trips on legitimate env-reading integrations, security skills, and library .eval calls). This is NOT an authoritative malicious verdict — re-scan with SkillSpector for the authoritative result. Run the authoritative scan →

Skillproof quality grade A

📇 This skill is in the Skillier index (curated · deduped · quality-filtered). Install Skillier to route & load it into your AI client.

Quality notes

Skill is large (~3692 tokens)
medium · quality · body
→ Tighten to the essential procedure; move long reference material to linked files.
No explicit trigger / 'when to use'
low · quality · body
→ Add a 'When to use' section or 'Use this when …' line listing trigger conditions.

About this skill

Cloudflare Browser Rendering with Puppeteer/Playwright. Use for screenshots, PDFs, web scraping, or encountering rendering errors, timeout issues, memory exceeded.

📄 Read the SKILL.md
---
name: cloudflare-browser-rendering
description: "Cloudflare Browser Rendering with Puppeteer/Playwright. Use for screenshots, PDFs, web scraping, or encountering rendering errors, timeout issues, memory exceeded."
license: MIT
metadata:
  version: "1.0.0"
  last_verified: "2025-11-27"
  puppeteer_version: "1.0.4"
  playwright_version: "1.0.0"
  workers_types_version: "4.20251125.0"
  wrangler_version: "4.50.0"
  production_tested: true
  errors_prevented: 6
  references_included: 6
  keywords:
    - browser rendering cloudflare
    - "@cloudflare/puppeteer"
    - "@cloudflare/playwright"
    - puppeteer workers
    - playwright workers
    - screenshot cloudflare
    - pdf generation workers
    - web scraping cloudflare
    - headless chrome workers
    - browser automation
    - puppeteer.launch
    - playwright.chromium.launch
    - browser binding
    - session management
    - puppeteer.sessions
    - puppeteer.connect
    - browser.close
    - browser.disconnect
    - XPath not supported
    - browser timeout
    - concurrency limit
    - keep_alive
    - page.screenshot
    - page.pdf
    - page.goto
    - page.evaluate
    - incognito context
    - session reuse
    - batch scraping
    - crawling websites
---
# Cloudflare Browser Rendering - Complete Reference

Production-ready knowledge domain for building browser automation workflows with Cloudflare Browser Rendering.

**Status**: Production Ready ✅
**Last Updated**: 2025-11-25
**Dependencies**: cloudflare-worker-base (for Worker setup)
**Latest Versions**: @cloudflare/puppeteer@1.0.4, @cloudflare/playwright@1.0.0, wrangler@4.50.0, @cloudflare/workers-types@4.20251125.0

---

## Table of Contents

1. [Quick Start (5 minutes)](#quick-start-5-minutes)
2. [Browser Rendering Overview](#browser-rendering-overview)
3. [Puppeteer API Reference](#puppeteer-api-reference)
4. [Playwright API Reference](#playwright-api-reference)
5. [Session Management](#session-management)
6. [Common Patterns](#common-patterns)
7. [Pricing & Limits](#pricing--limits)
8. [Known Issues Prevention](#known-issues-prevention)
9. [Production Checklist](#production-checklist)

---

## Quick Start (5 minutes)

### 1. Add Browser Binding

**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.

### 2. Install Puppeteer

```bash
bun add @cloudflare/puppeteer
```

### 3. Take Your First Screenshot

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

### 4. Deploy

```bash
bunx 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

---

## When to Load References

**Load immediately when user mentions**:

- `puppeteer-api.md` → "API reference", "Puppeteer methods", "Browser class", "Page methods", "complete API"
- `patterns.md` → "examples", "how to", "screenshot", "PDF", "scraping", "automation", "form filling"
- `session-management.md` → "sessions", "hibernation", "connection pooling", "state management", "Durable Objects"
- `pricing-and-limits.md` → "cost", "pricing", "limits", "quotas", "billing", "rate limits"
- `common-errors.md` → errors, debugging, "not working", troubleshooting, "issue #4", "issue #5", "issue #6"
- `puppeteer-vs-playwright.md` → "Playwright", "comparison", "which library", "differences"

**Load proactively when**:
- Building new automation → Load `patterns.md`
- Debugging errors → Load `common-errors.md`
- Optimizing costs → Load `pricing-and-limits.md`
- Managing sessions → Load `session-management.md`
- Need complete API → Load `puppeteer-api.md`

---

## 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

### Two Integration Methods

| Method | Best For | Complexity |
|--------|----------|-----------|
| **Workers Bindings** | Complex automation, custom workflows, session management | Advanced |
| **REST API** | Simple screenshot/PDF tasks | Simple |

**This skill covers Workers Bindings** (the advanced method with full Puppeteer/Playwright APIs).

### Puppeteer vs Playwright

| Feature | Puppeteer | Playwright |
|---------|-----------|------------|
| **API Familiarity** | Most popular | Growing adoption |
| **Package** | `@cloudflare/puppeteer@1.0.4` | `@cloudflare/playwright@1.0.0` |
| **Session Management** | ✅ Advanced APIs | ⚠️ Basic |
| **Browser Support** | Chromium only | Chromium only (Firefox/Safari not yet supported) |
| **Best For** | Screenshots, PDFs, scraping | Testing, frontend automation |

**Recommendation**: Use Puppeteer for most use cases. Playwright is ideal if you're already using it for testing.

---

## Puppeteer API Reference

**Core classes for browser automation**:

1. **Core Functions** - `launch()`, `connect()`, `sessions()`, `history()`, `limits()`
2. **Browser API** - `newPage()`, `sessionId()`, `close()`, `disconnect()`, `createBrowserContext()`
3. **Page API** - `goto()`, `screenshot()`, `pdf()`, `content()`, `setContent()`, `evaluate()`, `waitForSelector()`, `type()`, `click()`

**Quick Example:**
```typescript
const browser = await puppeteer.launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto("https://example.com");
const screenshot = await page.screenshot({ fullPage: true });
await browser.close();
```

**Load `references/puppeteer-api.md` when implementing browser automation, scraping, debugging Puppeteer-specific issues, or needing complete API signatures and method details.**

---

## Playwright API Reference

Playwright provides a similar API to Puppeteer with slight differences.

### Installation

```bash
bun add @cloudflare/playwright
```

### Basic Example

```typescript
import { env } from "cloudflare:test";
import { chromium } from "@cloudflare/playwright";

interface Env {
  BROWSER: Fetcher;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    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();

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

### Key Differences from Puppeteer

| Feature | Puppeteer | Playwright |
|---------|-----------|------------|
| **Import** | `import puppeteer from "@cloudflare/puppeteer"` | `import { chromium } from "@cloudflare/playwright"` |
| **Launch** | `puppeteer.launch(env.MYBROWSER)` | `chromium.launch(env.BROWSER)` |
| **Session API** | ✅ Advanced (sessions, history, limits) | ⚠️ Basic |
| **Auto-waiting** | Manual `waitForSelector()` | Built-in auto-waiting |
| **Selectors** | CSS only | CSS, text, XPath (via evaluate workaround) |

**Recommendation**: Stick with Puppeteer unless you have existing Playwright tests to migrate.

---

## Session Management

Browser sessions are managed using Durable Objects for state persistence across multiple requests. Sessions support hibernation, automatic cleanup, and concurrent connection handling.

**Key Patterns**:
- **Session Reuse** - Use `puppeteer.sessions()` and `puppeteer.connect()` to reuse browsers
- **Browser Contexts** - Isolate cookies/cache while sharing browser instance
- **Multiple Tabs** - Use tabs (`newPage()`) instead of multiple browsers for batch operations
- **Disconnect vs Close** - Use `disconnect()` to keep session alive, `close()` to terminate

**Load `references/session-management.md` for complete session lifecycle management, hibernation patterns, connection pooling strategies, and production examples.**

---

## Common Patterns

**6 production-ready browser automation patterns**:

1. **Screenshot with KV Caching** - Cache screenshots for high-traffic URLs, reduce browser usage
2. **PDF Generation from HTML** - Convert custom HTML to PDF for invoices, reports, documents
3. **Web Scraping with Structured Data** - Extract product information, prices, content from web pages
4. **Batch Scraping Multiple URLs** - Efficiently scrape multiple sites using tabs in single browser
5. **AI-Enhanced Scraping** - Combine Browser Rendering with Workers AI for adaptive data extraction
6. **Form Filling and Automation** - Automate login flows, form submissions, multi-step workflows

**Quick Example** (Screenshot with caching):
```typescript
const browser = await puppeteer.launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto(url);
const screenshot = await page.screenshot({ fullPage: true });
await env.CACHE.put(url, screenshot, { expirationTtl: 86400 });
await browser.close();
```

**Load `references/patterns.md` when implementing browser automation patterns, scraping, PDF generation, or needing complete production examples with error handling and optimizations.**

---

## Pricing & Limits

Browser Rendering charges based on CPU time (paid plans only). Free tier: 10 minutes/day. Paid tier: 10 hours/month included, then $0.09 per browser hour + $2.00 per concurrent browser above 10.

**Load `references/pricing-and-limits.md` for complete pricing tiers, quota details, rate limiting strategies, and cost optimization techniques.**

---

## Known Issues Prevention

This skill prevents **6 documented issues**. Top 3 critical errors detailed below:

---

### Issue #1: XPath Selectors Not Supported ⚠️

**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;
});
```

---

### Issue #2: Browser Binding Not Passed ⚠️

**Error:** "Cannot read properties of undefined (reading 'fetch')"
**Source:** https://developers.cloudflare.com/browser-rendering/faq/#cannot-read-properties-of-undefined-reading-fetch
**Why It Happens:** `puppeteer.launch()` called without browser binding
**Prevention:** Always pass `env.MYBROWSER` to launch

**Solution:**
```typescript
// ❌ Missing browser binding
const browser = await puppeteer.launch(); // Error!

// ✅ Pass binding
const browser = aw

… (truncated)
Scan or optimize your own skill →

Want a live grade + an embeddable README badge? Run your skill through the free scanner.

Graded independently by Skillproof — nothing to sell the author. Quality is mechanical + corpus-grounded; safety flags are heuristic (builtin+triage), not a malicious verdict.