cloudflare-browser-rendering について
Cloudflare Browser Rendering でブラウザー自動化を構築する開発者向けのリファレンスです。Workers バインディングによる Puppeteer と Playwright の利用、セッション管理、よくある問題を扱います。
主な機能
- 01Puppeteer と Playwright の解説
- 02ブラウザーセッションの再利用と分離
- 03スクリーンショットと PDF の生成
- 04動的ページのスクレイピングと操作
- 05既知の問題と制限の説明
活用シーン
- 01ページのスクリーンショットや PDF の作成
- 02動的な Web サイトからのデータ抽出
- 03フォームやフロントエンドテストの自動化
MCP 対応
Playwright MCP のサポートと Playwright MCP サーバーへの言及がありますが、主な内容は Workers バインディングを使ったブラウザー自動化です。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jezweb/ claude-skills --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.mdrules/cloudflare-browser-rendering.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.ts詳細
Cloudflare Browser Rendering - 完全リファレンス
Cloudflare Browser Rendering を使用してブラウザー自動化ワークフローを構築するための、本番環境対応ナレッジドメインです。
ステータス:本番環境対応 ✅ 最終更新日:2026-01-21 依存関係:cloudflare-worker-base(Worker のセットアップ用) 最新バージョン:@cloudflare/[email protected]、@cloudflare/[email protected]、[email protected]
最近の更新(2025):
- 2025年9月:Playwright v1.55 GA、Stagehand フレームワークのサポート(Workers AI)、/links の excludeExternalLinks パラメーター
- 2025年8月:課金 GA(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分)
- Browser Rendering の概要
- Puppeteer API リファレンス
- Playwright API リファレンス
- セッション管理
- 一般的なパターン
- 料金と制限
- 既知の問題の防止
- 本番環境チェックリスト
クイックスタート(5分)
1. ブラウザーバインディングを追加する
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 とポリフィルが必要です。
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
重要:
puppeteer.launch()には必ずenv.MYBROWSERを渡してください(undefined は不可)- 完了したら必ず
browser.close()を呼び出してください(セッションを再利用する場合はbrowser.disconnect()を使用します) nodejs_compat互換性フラグを使用してください
Browser Rendering の概要
Browser Rendering とは?
Cloudflare Browser Rendering は、Cloudflare のグローバルネットワーク上で動作するヘッドレス Chromium ブラウザーを提供します。Puppeteer や Playwright などの使い慣れたツールで、ブラウザータスクを自動化できます。
- スクリーンショット - Web ページの画面イメージをキャプチャします
- PDF 生成 - HTML/URL を PDF に変換します
- Web スクレイピング - 動的な Web サイトからコンテンツを抽出します
- テスト - フロントエンドテストを自動化します
- クローリング - 複数ページにわたるワークフローを移動します
2つの統合方法
| 方法 | 最適な用途 | 複雑さ |
|---|---|---|
| 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 を使用している場合は、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 サーバーを利用できます
- 最新バージョン:Playwright v1.57 をサポート(2026 年 1 月更新)
推奨事項:セッションを再利用するパターンには 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/キャッシュの分離)
ブラウザーを共有しつつ Cookie/キャッシュを分離するには、browser.createBrowserContext() を使用します。
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 個のブラウザーを起動します(同時実行数を無駄に消費します)
✅ 良い例:1 つのブラウザーで、Promise.all() と browser.newPage() を使って 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日
無料プラン:1日あたり10分、同時実行数3、1分あたり3回の起動、タイムアウト60秒
有料プラン:月あたり10時間を含む(超過分は1時間あたり$0.09)、同時実行数30(超過分はブラウザー1台あたり$2.00)、1分あたり30回の起動、タイムアウト60秒~10分
同時実行数の計算:日ごとのピーク使用量の月間平均(例:ブラウザーの平均数が35台の場合、(35 - 30台の包含分)× $2.00 = 月額$10.00)
レート制限:固定の毎秒補充レートで適用されます(バーストには対応していません)。1分あたり30リクエストの場合、2秒ごとに1リクエストです。クォータが未使用でも、30件のリクエストを一度に送信することはできません。起動前に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));
}
既知の問題の予防
このスキルは、文書化されている8つの問題を防ぎます:
問題 #1:XPath セレクターはサポートされていない
エラー:「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 セレクター、または 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:ブラウザーバインディングが渡されていない(Fetcher 型の混同)
エラー:「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()のラッパーに渡します
解決策:
// ❌ 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 の型の説明:
interface Env {
MYBROWSER: Fetcher; // It's a Fetcher, not a Browser!
}
問題 #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:同時実行数の上限に到達
エラー:「Rate limit exceeded」または新しいブラウザーの起動に失敗する
**出典:**https://developers.cloudflare.com/browser-rendering/platform/limits/、[変更履歴 2025-09-25](https://developers.cloudflare.com/browser-rendering/changelog/)
**発生する理由:**同時実行可能なブラウザー数の上限を超えています(2025年9月時点で無料プランは3台、有料プランは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 のスキップルールを作成します(Bot Management の利用には Enterprise プランが必要です)
解決策:
// ❌ 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
**重要:**Free/Pro/Business プランでは、自分のサイトであってもボット検出を回避できません。WAF の許可リスト設定には、Bot Management を含む Enterprise プランが必要です。
問題 #7:page.evaluate() の関数名インジェクション(__name エラー)
エラー: 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 以降で修正済み)
解決策:
// ❌ 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
注: 複雑なコールバックを指定した page.waitForSelector() にも影響します。wrangler 3.83.0 以降で修正済みです(2024 年 11 月)。
問題 #8:waitForSelector() のタイムアウト動作が変更
エラー: 無期限の待機に依存していたコードがタイムアウトする
ソース: Changelog 2026-01-07
発生する理由: 以前は、セレクターが見つからない場合に waitForSelector() はタイムアウトせず、無期限に停止していました。この動作が修正され、タイムアウト値が正しく適用されるようになりました。
回避方法: 常に明示的なタイムアウトを設定し、タイムアウトエラーを処理してください。
該当範囲: 無期限の待機に依存していた、2026 年 1 月より前に作成されたすべてのコード
解決策:
// ❌ 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
注: これは破壊的な修正(動作の変更)です。無期限の待機に依存していたコードは、タイムアウトしてエラーをスローするようになります。常に TimeoutError を適切に処理してください。
本番環境へのデプロイ前チェックリスト
Browser Rendering Workers を本番環境にデプロイする前に、以下を確認してください。
設定
- wrangler.jsonc でブラウザーバインディングを設定済み
- nodejs_compat フラグを有効化済み(Browser Rendering に必須)
- タスクに 60 秒以上かかる場合、キープアライブのタイムアウトを設定済み
- 必要に応じて、ローカル開発用のリモートバインディングを有効化済み
エラー処理
- レート制限に対する再試行ロジックを実装済み
- page.goto() のタイムアウト処理を実装済み
- try-finally ブロックでブラウザーをクリーンアップ
- ブラウザーの起動前に同時実行数の上限を確認
- ブラウザーが利用できない場合のグレースフルデグラデーションを実装済み
パフォーマンス
- トラフィックの多いルートでセッションの再利用を実装済み
- 複数のブラウザーではなく、複数のタブを使用
- セッションを分離するためにシークレットコンテキストを使用
- 繰り返し生成されるスクリーンショット/PDF にKV キャッシュを使用
- ブラウザーの利用率を最大化するためにバッチ操作を使用
モニタリング
- デバッグ用にブラウザーセッション ID をログに記録
- 請求額の見積もり用にブラウザーの利用時間を追跡
- puppeteer.limits() で同時実行数の使用状況を監視
- レート制限エラーのアラートを設定
- https://dash.cloudflare.com/?to=/:account/workers/browser-rendering でダッシュボードを監視
セキュリティ
- SSRF を防ぐためにURL の入力を検証
- 悪用を防ぐためにタイムアウトの上限を設定
- 公開エンドポイントにレート制限を設定
- 機密性の高いスクレイピングエンドポイントに認証を設定
- 独自のゾーンをスクレイピングする場合はWAF ルールを設定
テスト
- さまざまなページサイズでスクリーンショットの取得をテスト
- カスタム HTML でPDF の生成をテスト
- 動的コンテンツを使用して(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/[email protected]- Workers 向け Puppeteer[email protected]+- Cloudflare CLI
任意:
@cloudflare/[email protected]- Workers 向け Playwright(代替)@cloudflare/[email protected]+- TypeScript 型定義
関連スキル:
cloudflare-worker-base- Hono を使った Worker のセットアップcloudflare-kv- スクリーンショット用の KV キャッシュcloudflare-r2- 生成ファイル用の R2 ストレージcloudflare-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/
パッケージバージョン(2026-01-21 確認済み)
{
"dependencies": {
"@cloudflare/puppeteer": "^1.0.4"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20251014.0",
"wrangler": "^4.59.3"
}
}
代替(Playwright):
{
"dependencies": {
"@cloudflare/playwright": "^1.1.0"
}
}
注: Playwright v1.1.0 には Playwright v1.57(2026年1月)のサポートが含まれています。Wrangler 3.83.0+ では page.evaluate() の __name 挿入バグが修正されています。
トラブルシューティング
問題:「Cannot read properties of undefined (reading 'fetch')」
解決策: browser binding を 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 で browser binding が設定されていることを確認してください
- 公式ドキュメントを確認してください:https://developers.cloudflare.com/browser-rendering/
nodejs_compat互換性フラグが有効になっていることを確認してください