Playwright ↗ は Microsoft が開発したオープンソースパッケージで、ブラウザー自動化ができます。フロントエンドテストの作成、スクリーンショットの取得、ページのクロールによく使われます。
Workers チームは、Cloudflare Workers と Browser Run で動くように改変した Playwright のバージョン ↗ をフォークしています。
このバージョンはオープンソースで、Cloudflare の Playwright フォーク ↗ で確認できます。npm パッケージは npmjs ↗ から @cloudflare/playwright ↗ としてインストールできます。
npm i -D @cloudflare/playwrightyarn add -D @cloudflare/playwrightpnpm add -D @cloudflare/playwrightbun add -d @cloudflare/playwrightこの 例 ↗ では、todomvc ↗ アプリケーションを使い、Cloudflare Worker で Playwright テストを実行します。
手順を省略してすぐに始めたい場合は、下の Deploy to Cloudflare を選びます。
Wrangler の設定ファイルに browser binding があることを確認します。
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "cloudflare-playwright-example",
"main": "src/index.ts",
"workers_dev": true,
"compatibility_flags": ["nodejs_compat"],
// Set this to today's date
"compatibility_date": "2026-09-20",
"upload_source_maps": true,
"browser": {
"binding": "MYBROWSER",
},
}"$schema" = "./node_modules/wrangler/config-schema.json"
name = "cloudflare-playwright-example"
main = "src/index.ts"
workers_dev = true
compatibility_flags = [ "nodejs_compat" ]
# Set this to today's date
compatibility_date = "2026-09-20"
upload_source_maps = true
[browser]
binding = "MYBROWSER"npm パッケージをインストールします。
npm i -D @cloudflare/playwrightyarn add -D @cloudflare/playwrightpnpm add -D @cloudflare/playwrightbun add -d @cloudflare/playwrightPlaywright の使い方の例を見ていきます。
ブラウザー自動化で Web ページのスクリーンショットを撮るのは、よくある用途です。このスクリプトは、ブラウザーに https://demo.playwright.dev/todomvc ↗ へ移動させ、いくつかの項目を作成し、ページのスクリーンショットを撮り、レスポンスとして画像を返します。
import { launch } from "@cloudflare/playwright";
export default {
async fetch(request: Request, env: Env) {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
const img = await page.screenshot();
await browser.close();
return new Response(img, {
headers: {
"Content-Type": "image/png",
},
});
},
};Playwright の Trace は、ワークフロー実行の詳細ログです。ユーザーのクリックやナビゲーション、ページのスクリーンショット、コンソールメッセージなどを記録し、デバッグに使います。開発者は trace.zip ファイルを ローカルで開く ↗ か、GUI ツールの Playwright Trace Viewer ↗ にアップロードしてデータを調べられます。
Trace ファイルを生成する Worker の例です。
import fs from "fs";
import { launch } from "@cloudflare/playwright";
export default {
async fetch(request: Request, env: Env) {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
// Start tracing before navigating to the page
await page.context().tracing.start({ screenshots: true, snapshots: true });
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
// Stop tracing and save the trace to a zip file
await page.context().tracing.stop({ path: "trace.zip" });
await browser.close();
const file = await fs.promises.readFile("trace.zip");
return new Response(file, {
status: 200,
headers: {
"Content-Type": "application/zip",
},
});
},
};Playwright のよくある用途のひとつがソフトウェアテストです。Playwright の API にはテストアサーション機能があります。詳細は Playwright ドキュメントの Assertions ↗ を参照してください。todomvc ↗ のデモページに対して expect() テストアサーションを行う Worker の例です。
import { launch } from "@cloudflare/playwright";
import { expect } from "@cloudflare/playwright/test";
export default {
async fetch(request: Request, env: Env) {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
await expect(page.getByTestId("todo-title")).toHaveCount(TODO_ITEMS.length);
await Promise.all(
TODO_ITEMS.map((value, index) =>
expect(page.getByTestId("todo-title").nth(index)).toHaveText(value),
),
);
},
};Playwright は storage state ↗ に対応しており、Cookie やその他のストレージデータを取得・永続化できます。この例では、storage state を使って Cookie やその他のストレージデータを Workers KV に保存します。
まず、KV 名前空間があることを確認します。次のコマンドで新規作成できます。
npx wrangler kv namespace create KV次に、Wrangler の設定ファイルに KV 名前空間を追加します。
{
"name": "storage-state-examples",
"main": "src/index.ts",
"compatibility_flags": ["nodejs_compat"],
// Set this to today's date
"compatibility_date": "2026-09-20",
"browser": {
"binding": "MYBROWSER",
},
"kv_namespaces": [
{
"binding": "KV",
"id": "<YOUR-KV-NAMESPACE-ID>",
},
],
}name = "storage-state-examples"
main = "src/index.ts"
compatibility_flags = [ "nodejs_compat" ]
# Set this to today's date
compatibility_date = "2026-09-20"
[browser]
binding = "MYBROWSER"
[[kv_namespaces]]
binding = "KV"
id = "<YOUR-KV-NAMESPACE-ID>"これで、storage state を使って Cookie やその他のストレージデータを KV に保存できます。
// gets persisted storage state from KV or undefined if it does not exist
const storageStateJson = await env.KV.get("storageState");
const storageState = storageStateJson
? ((await JSON.parse(
storageStateJson,
)) as BrowserContextOptions["storageState"])
: undefined;
await using browser = await launch(env.MYBROWSER);
// creates a new context with storage state persisted in KV
await using context = await browser.newContext({ storageState });
await using page = await context.newPage();
// do some actions on the page that may update client-side storage
// gets updated storage state: cookies, localStorage, and IndexedDB
const updatedStorageState = await context.storageState({ indexedDB: true });
// persists updated storage state in KV
await env.KV.put("storageState", JSON.stringify(updatedStorageState));browser.close() を省略すると、ブラウザーインスタンスは開いたままになり、再接続して 再利用 できます。ただし、デフォルトでは 1 分間操作がないと自動で閉じます。ミリ秒単位の keep_alive オプションで、このアイドル時間を最大 10 分まで延長できます。
const browser = await playwright.launch(env.MYBROWSER, { keep_alive: 600000 });上記を使うと、操作がなくてもブラウザーは最大 10 分間開いたままになります。
Browser Run Worker の性能を上げるいちばんの方法は、使い終わったあともブラウザーを開いたままにし、新しいリクエストのたびにそのセッションへ接続して再利用することです。Playwright の browser.close ↗ の扱いは Puppeteer と異なります。Playwright では、connect セッションで取得したブラウザーは切断され、launch セッションで取得したブラウザーは閉じます。
import { env } from "cloudflare:workers";
import { acquire, connect } from "@cloudflare/playwright";
async function reuseSameSession() {
// acquires a new session
const { sessionId } = await acquire(env.BROWSER);
for (let i = 0; i < 5; i++) {
// connects to the session that was previously acquired
const browser = await connect(env.BROWSER, sessionId);
// ...
// this will disconnect the browser from the session, but the session will be kept alive
await browser.close();
}
}Playwright でカスタム User-Agent を指定するには、browser.newContext() で新しいブラウザーコンテキストを作るときのオプションに設定します。このコンテキストから作る以降のページは、新しい User-Agent を使います。対象サイトが User-Agent に応じて別のコンテンツを返す場合に便利です。
const context = await browser.newContext({
userAgent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
});wrangler dev または vite dev でローカル開発すると、Chrome はデフォルトでヘッドレスモードで起動します。Chrome を表示あり(ヘッドフル)で起動するには、X_BROWSER_HEADFUL 環境変数を設定します。
X_BROWSER_HEADFUL=true npx wrangler devCloudflare Vite plugin を使う場合は次のとおりです。
X_BROWSER_HEADFUL=true npx vite devブラウザーウィンドウが開き、Playwright の自動化をリアルタイムで確認できます。ナビゲーション、要素の選択、ページ操作のデバッグがしやすくなります。
ブラウザーセッションの管理を簡単にするため、Playwright API に新しいメソッドを追加しています。
playwright.sessions() は、現在実行中のセッションを一覧します。次のような出力になります。
[
{
"connectionId": "2a2246fa-e234-4dc1-8433-87e6cee80145",
"connectionStartTime": 1711621704607,
"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
"startTime": 1711621703708
},
{
"sessionId": "565e05fb-4d2a-402b-869b-5b65b1381db7",
"startTime": 1711621703808
}
]セッション 478f4d7d-e943-40f6-a414-837d3736a1dc には稼働中の Worker 接続(connectionId=2a2246fa-e234-4dc1-8433-87e6cee80145)があり、セッション 565e05fb-4d2a-402b-869b-5b65b1381db7 は空きです。接続が有効なあいだ、ほかの Worker はそのセッションに接続できません。
playwright.history() は、開いているものと閉じたものの両方を含む最近のセッションを一覧します。現在の利用状況を把握するのに役立ちます。
[
{
"closeReason": 2,
"closeReasonText": "BrowserIdle",
"endTime": 1711621769485,
"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
"startTime": 1711621703708
},
{
"closeReason": 1,
"closeReasonText": "NormalClosure",
"endTime": 1711123501771,
"sessionId": "2be00a21-9fb6-4bb2-9861-8cd48e40e771",
"startTime": 1711123430918
}
]セッション 2be00a21-9fb6-4bb2-9861-8cd48e40e771 はクライアントが browser.close() で明示的に閉じ、セッション 478f4d7d-e943-40f6-a414-837d3736a1dc は最大アイドル時間に達して閉じました(制限 を参照)。
ダッシュボードでもこの情報を確認できますが、少し遅れることがあります。
playwright.limits() は、現在の上限を一覧します。
{
"activeSessions": [
{ "id": "478f4d7d-e943-40f6-a414-837d3736a1dc" },
{ "id": "565e05fb-4d2a-402b-869b-5b65b1381db7" }
],
"allowedBrowserAcquisitions": 1,
"maxConcurrentSessions": 2,
"timeUntilNextAllowedBrowserAcquisition": 0
}activeSessionsは、現在開いているセッションの ID を一覧します。maxConcurrentSessionsは、同時に開けるブラウザー数を定義します。allowedBrowserAcquisitionsは、設定されているレート 制限 に照らして、新しいブラウザーセッションを開けるかどうかを示します。timeUntilNextAllowedBrowserAcquisitionは、次のブラウザーを起動できるまでの待ち時間です。
Playwright API の全体は、Playwright API ドキュメント ↗ にあります。
次の機能はまだ完全には対応していません。現在、対応を進めています。
- Playwright Test ↗(Assertions ↗ を除く)
- Components ↗
- Firefox ↗、Android ↗、Electron ↗、および Chrome の異なるバージョン
- Videos ↗
これは網羅的な一覧ではありません。元の機能セットとの差を埋める作業が進むにつれて、内容はすぐに変わります。完全に対応している機能の細かい最新一覧は、最新のテスト結果 ↗ でも確認できます。