既定では、Browser Sessions のリクエストごとに新しいブラウザーインスタンスが起動します。セッションを再利用すると、既存のブラウザーに再接続するため、コールドスタート時間をなくし、性能を高められます。
この機能は Browser Sessions(Puppeteer、Playwright、CDP)に適用されます。Quick Actions はセッションのライフサイクルを自動で扱います。
セッションを再利用する方法は 2 つあります。
- 切断して再接続する(このページで説明します):
browser.close()ではなくbrowser.disconnect()を使い、ブラウザーを生かしたまま次のリクエストで再接続します。利用可能なブラウザーセッションであればよい、ステートレスなワークロードに向いています。 - Durable Objects: Durable Object 内に長時間稼働するブラウザーを保持し、ステートフルにセッションを管理します。リクエストをまたいで状態を保つ、または特定のユーザーを特定のブラウザーインスタンスへ振り分けたい場合に向いています。
Cloudflare Workers は、インフラの設定や運用なしに新しいアプリケーションを作ったり、既存のものを拡張したりできるサーバーレス実行環境です。Worker アプリケーションは、スクリーンショット取得などの操作をヘッドレスブラウザーと行うための入れ物です。
次のコマンドで、browser-worker という新しい Worker プロジェクトを作成します。
npm create cloudflare@latest -- browser-workeryarn create cloudflare browser-workerpnpm create cloudflare@latest browser-workerセットアップでは、次のオプションを選びます。
- What would you like to start with? では、
Hello World exampleを選びます。 - Which template would you like to use? では、
Worker onlyを選びます。 - Which language do you want to use? では、
TypeScriptを選びます。 - Do you want to use git for version control? では、
Yesを選びます。 - Do you want to deploy your application? では、
Noを選びます(デプロイ前にいくつか変更します)。
browser-worker ディレクトリで、Cloudflare の Puppeteer フォーク をインストールします。
npm i -D @cloudflare/puppeteeryarn add -D @cloudflare/puppeteerpnpm add -D @cloudflare/puppeteerbun add -d @cloudflare/puppeteer3. Wrangler 設定ファイル を構成する
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "browser-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-09-20",
"compatibility_flags": ["nodejs_compat"],
"browser": {
"binding": "MYBROWSER",
},
}"$schema" = "./node_modules/wrangler/config-schema.json"
name = "browser-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-09-20"
compatibility_flags = [ "nodejs_compat" ]
[browser]
binding = "MYBROWSER"次のスクリプトは、まず現在稼働中のセッションを取得します。Worker がまだ接続していないセッションがあれば、ランダムなセッション ID を選び、puppeteer.connect(..) で接続を試みます。失敗した場合、または稼働中のセッションがなかった場合は、puppeteer.launch(..) で新しいブラウザーセッションを起動します。その後、Web サイトへ移動して DOM を取得します。完了したら browser.disconnect() で切断し、ほかの Worker が接続できるようにします。
ブラウザーがアイドル(コマンドを受け取らない状態)のまま、現在の 上限 を超えると自動で閉じます。セッションを維持するには、1 分あたり十分なリクエスト数が必要です。
import puppeteer from "@cloudflare/puppeteer";
export default {
async fetch(request, env) {
const url = new URL(request.url);
let reqUrl = url.searchParams.get("url") || "https://example.com";
reqUrl = new URL(reqUrl).toString(); // normalize
// Pick random session from open sessions
let sessionId = await this.getRandomSession(env.MYBROWSER);
let browser, launched;
if (sessionId) {
try {
browser = await puppeteer.connect(env.MYBROWSER, sessionId);
} catch (e) {
// another worker may have connected first
console.log(`Failed to connect to ${sessionId}. Error ${e}`);
}
}
if (!browser) {
// No open sessions, launch new session
browser = await puppeteer.launch(env.MYBROWSER);
launched = true;
}
sessionId = browser.sessionId(); // get current session id
// Do your work here
const page = await browser.newPage();
const response = await page.goto(reqUrl);
const html = await response.text();
// All work done, so free connection (IMPORTANT!)
browser.disconnect();
return new Response(
`${launched ? "Launched" : "Connected to"} ${sessionId} \n-----\n` + html,
{
headers: {
"content-type": "text/plain",
},
},
);
},
// Pick random free session
// Other custom logic could be used instead
async getRandomSession(endpoint) {
const sessions = await puppeteer.sessions(endpoint);
console.log(`Sessions: ${JSON.stringify(sessions)}`);
const sessionsIds = sessions
.filter((v) => {
return !v.connectionId; // remove sessions with workers connected to them
})
.map((v) => {
return v.sessionId;
});
if (sessionsIds.length === 0) {
return;
}
const sessionId =
sessionsIds[Math.floor(Math.random() * sessionsIds.length)];
return sessionId;
},
};import puppeteer from "@cloudflare/puppeteer";
interface Env {
MYBROWSER: Fetcher;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
let reqUrl = url.searchParams.get("url") || "https://example.com";
reqUrl = new URL(reqUrl).toString(); // normalize
// Pick random session from open sessions
let sessionId = await this.getRandomSession(env.MYBROWSER);
let browser, launched;
if (sessionId) {
try {
browser = await puppeteer.connect(env.MYBROWSER, sessionId);
} catch (e) {
// another worker may have connected first
console.log(`Failed to connect to ${sessionId}. Error ${e}`);
}
}
if (!browser) {
// No open sessions, launch new session
browser = await puppeteer.launch(env.MYBROWSER);
launched = true;
}
sessionId = browser.sessionId(); // get current session id
// Do your work here
const page = await browser.newPage();
const response = await page.goto(reqUrl);
const html = await response!.text();
// All work done, so free connection (IMPORTANT!)
browser.disconnect();
return new Response(
`${launched ? "Launched" : "Connected to"} ${sessionId} \n-----\n` + html,
{
headers: {
"content-type": "text/plain",
},
},
);
},
// Pick random free session
// Other custom logic could be used instead
async getRandomSession(endpoint: puppeteer.BrowserWorker): Promise<string> {
const sessions: puppeteer.ActiveSession[] =
await puppeteer.sessions(endpoint);
console.log(`Sessions: ${JSON.stringify(sessions)}`);
const sessionsIds = sessions
.filter((v) => {
return !v.connectionId; // remove sessions with workers connected to them
})
.map((v) => {
return v.sessionId;
});
if (sessionsIds.length === 0) {
return;
}
const sessionId =
sessionsIds[Math.floor(Math.random() * sessionsIds.length)];
return sessionId!;
},
};puppeteer.sessions() 以外にも、セッション管理 を助けるメソッドを追加しています。
npx wrangler dev を実行し、Worker をローカルでテストします。
テストするには、次の URL を開きます。
<LOCAL_HOST_URL>/?url=https://example.comnpx wrangler deploy を実行して Worker を Cloudflare のグローバルネットワークへデプロイし、次の URL を開きます。
<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/?url=https://example.com