ブログサイトのロゴsui Tech Blog

Cloudflareのグローバルネットワーク上でヘッドレスChromeを実行するBrowser Rendering

Cloudflare Browser RenderingのREST APIを使って、WebページをMarkdownに変換するAPIを構築する方法を解説します。不要な要素の削除やリソース最適化など、実践的な実装例を紹介します。

Cloudflare Browser Rendering は、ブラウザの自動化や Web スクレイピング、テスト、そしてコンテンツ生成のために、Cloudflare のグローバルネットワーク上でヘッドレス Chrome を実行できる機能です。主な用途としては、スクレイピングしたり、Web ページを Markdown に変換したりといったことが挙げられます。

今回作成するもの

この記事では、Browser Rendering の機能を活用して、URL を渡すと記事の内容を Markdown 形式で返してくれる API を作成します。

この機能を利用するには、大きく分けて 2 つのアプローチがあります。

1 つ目は、Puppeteer や Playwright を使用する方法です。こちらはクリックやスクロールといった複雑なブラウザ操作が必要な場合や、MCP (Model Context Protocol) を活用したエージェントフローを組む場合に適しています。

2 つ目は、今回使用する REST API の TypeScript SDK を使用する方法です。こちらはスクリーンショットの撮影や PDF 生成、そして今回の目的である HTML コンテンツの抽出といった、シンプルでステートレスなタスクに最適化されています。

今回は「URL を渡してテキストを取得する」という単一のタスクであり、複雑なブラウザ操作は必要ありません。そのため、より軽量で手軽に実装できる REST API の方を選択しました。

開発に至った背景

なぜ今回このような API を作ろうと思ったのか、その背景にある私個人の課題感について少しお話しさせてください。

私は日々の情報の保管庫として Obsidian を使っているのですが、line_to_obsidian というツールを使ってスマホから URL を送信し、そのページを後で Obsidian Web Clipper を使って Markdown に落とし込むというフローを実践しています。

github.com

GitHub - onikun94/line_to_obsidian

Contribute to onikun94/line_to_obsidian development by creating an account on GitHub.

obsidian.md

Obsidian Web Clipper

Highlight and capture web pages in your favorite browser. Save anything and everything with just one click.

この記事自体も、「X (旧 Twitter) で Cloudflare のエンドポイントを叩くと Markdown が返ってくるらしい」という情報を元に、様々な情報を集めて作成しています。しかし、このようなブログ記事のネタ集めだったり、日々の「これ後で読んでおこうかな」といった情報を抽出したりする過程で、ある 1 つの問題が発生するのです。

積読における期待値のズレ

そもそも「後で読もう」とか「今読もう」となった時に、「あれ、この記事思ってたのと違うな…」とか「いや、これ見たことあるな」といった記事だった場合、拍子抜けしてしまうことがあります。

本来あるべき姿は全てに目を通すことなんですが、例えば X (旧 Twitter) などで流れてきて「これは良さそうだな」と思い、今は別の作業中だから後で読もうとした時。あるいは Zenn やはてなブックマークなどでタイトルと冒頭だけ見て保存した時。そういった時に、後で最後まで読んでみるとがっかりする事態はなるべく避けたいなと思っています。

要約のための下準備

この「がっかり」を防ぐには、記事を保存した段階で AI に要約してもらい、読むべきかどうかの判断材料にできれば理想的です。

しかし、ここで技術的な課題が 1 つあります。AI に要約させるためには、まずその Web ページから「本文のテキストデータだけ」をきれいに抽出して渡してあげる必要があるのです。

Safari などのブラウザでページを開いて、手動でコピーして ChatGPT に貼り付けることもできますが、毎回それを行うのはあまりに億劫です。移動中や休憩時間の短い間にスマホで「後で読む」ボタンを押すようなシーンでは、その場で手動コピーなどしていられません。

そこで今回の Cloudflare Browser Rendering の出番です。
この機能を使えば、URL を送信するだけで、Web ページを Markdown (AI が理解しやすいテキスト形式) に変換して返してくれます。これを使って本文を自動で取得する API さえ作ってしまえば、あとはそれを AI に投げるだけで、読みたかった記事の要約フローが完成します。

今回はその第一歩として、まずは「URL を渡して Markdown を取得する」部分の実装から始めていきましょう。

始める前に

始める前に、次の権限を持つカスタム API トークンを作成してください。

必要な環境変数は以下の通りです。

api_token の場合、テンプレートの「Cloudflare Workers を編集する」から browser-rendering-test-token という名前で作成し、権限は「ブラウザレンダリング」を選択します。最小権限の法則に基づき、この権限だけを付与するようにしましょう。

作成されたトークンを使って POST リクエストを送り、成功したら設定完了です。

curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/tokens/verify" \
-H "Authorization: Bearer {api_token}"
{"result":{"id":"{id}","status":"active","not_before":"2025-11-24T00:00:00Z","expires_on":"2025-12-01T23:59:59Z"},"success":true,"errors":[],"messages":[{"code":10000,"message":"This API Token is valid and active","type":null}]}%  

それでは実際に /markdown エンドポイントを使用して、Web 記事の情報を Markdown で取得する API を作成し、記事内容を取得していきましょう。

以下のコマンドでセットアップをしていきます。アプリケーション名はわかりやすく browser-rendering-markdown にしておきます。シンプルな構成でよいので、以下の設定を選択してください。

  • For What would you like to start with?, choose Hello World example.
  • For Which template would you like to use?, choose Worker only.
  • For Which language do you want to use?, choose TypeScript.
  • For Do you want to use git for version control?, choose Yes.
  • For Do you want to deploy your application?, choose No (we will be making some changes before deploying).
developers.cloudflare.com

CLI

Set up and deploy your first Worker with Wrangler, the Cloudflare Developer Platform CLI.

% pnpm create cloudflare@latest browser-rendering-markdown
 
──────────────────────────────────────────────────────────────────────────────────────────────────────────
👋 Welcome to create-cloudflare v2.55.1!
🧡 Let's get started.
📊 Cloudflare collects telemetry about your usage of Create-Cloudflare.
 
Learn more at: https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/telemetry.md
──────────────────────────────────────────────────────────────────────────────────────────────────────────
 
╭ Create an application with Cloudflare Step 1 of 3

├ In which directory do you want to create your application?
│ dir ./browser-rendering-markdown

├ What would you like to start with?
│ category Hello World example

├ Which template would you like to use?
│ type Worker only

├ Which language do you want to use?
│ lang TypeScript

├ Copying template files
│ files copied to project directory

├ Updating name in `package.json`
│ updated `package.json`

├ Installing dependencies
│ installed via `pnpm install`

╰ Application created 
 
╭ Configuring your application for Cloudflare via `wrangler setup` Step 2 of 3

├ Installing wrangler A command line tool for building Cloudflare Workers
│ installed via `pnpm install wrangler --save-dev`

├ Retrieving current workerd compatibility date
│ compatibility date 2025-11-21

├ Generating types for your application
│ generated to `./worker-configuration.d.ts` via `pnpm run cf-typegen`

├ Do you want to use git for version control?
│ yes git

├ Initializing git repo
│ initialized git

├ Committing new files
│ git commit

╰ Application configured 
 
╭ Deploy with Cloudflare Step 3 of 3

├ Do you want to deploy your application?
│ no deploy via `pnpm run deploy`

╰ Done 
 
────────────────────────────────────────────────────────────
🎉  SUCCESS  Application created successfully!
 
💻 Continue Developing
Change directories: cd browser-rendering-markdown
Deploy: pnpm run deploy
 
📖 Explore Documentation
https://developers.cloudflare.com/workers
 
🐛 Report an Issue
https://github.com/cloudflare/workers-sdk/issues/new/choose
 
💬 Join our Community
https://discord.cloudflare.com
────────────────────────────────────────────────────────────

実際のプロジェクトへ移動します。

dev % cd browser-rendering-markdown 
browser-rendering-markdown % code .

実装

サンプルコードと同様に、Workers の API を叩いて実装していきましょう。今回の環境は Cloudflare Workers で実践しているため、環境変数は process.env ではなく .dev.vars の中に入れていきます。

必要な環境変数を取得し、pnpm run cf-typegen で型定義ファイルを更新します。

CLOUDFLARE_API_TOKEN=
CLOUDFLARE_ACCOUNT_ID=

Cloudflare Workers の初期セットアップ段階では、必要なモジュールが足りていないため、以下のコマンドを実行して Cloudflare のモジュールをインストールします。

pnpm add cloudflare

クエリパラメーターに設定されている URL にアクセスして、Markdown を返却するコードを実装します。
env.CLOUDFLARE_ACCOUNT_ID は、先ほど環境変数を設定した後に型定義ファイルを更新することで、env プロパティ経由で環境変数にアクセスすることが可能になります。

import Cloudflare from 'cloudflare';
 
export default {
  async fetch(request, env, _ctx): Promise<Response> {
    const url = new URL(request.url);
    const targetUrl = url.searchParams.get('url');
 
    if (!targetUrl) {
      return new Response('URL parameter is required', { status: 400 });
    }
 
    const client = new Cloudflare({
      apiToken: env.CLOUDFLARE_API_TOKEN,
    });
    const markdown = await client.browserRendering.markdown.create({
      account_id: env.CLOUDFLARE_ACCOUNT_ID,
      url: targetUrl,
    });
    return new Response(markdown, {
      status: 200,
      headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
    });
  },
} satisfies ExportedHandler<Env>;

それでは実際にアクセスしてみましょう。例えば Zenn の記事などではどうなるか試してみます。
取得する際に使った URL は、以前プチバズリしたこの記事です。

zenn.dev

GASを使うと社内だけで使えるWebサイトが簡単に作れるの知ってました?

ローカルサーバーを立ち上げて、 http://localhost:8787/?url=https://zenn.dev/sui_water/articles/502a6ef30b79e1 にアクセスすると、無事 Markdown が取得できました。

👀
 
# GASを使うと社内だけで使えるWebサイトが簡単に作れるの知ってました?
 
2025/11/03に公開
 
147
 
102
 
[![](https://storage.googleapis.com/zenn-user-upload/topics/f13e758fdb.png)TypeScript](https://zenn.dev/sui_water/articles/502a6ef30b79e1/topics/typescript)[![](https://storage.googleapis.com/zenn-user-upload/topics/d82fbd6fb0.png)GAS](https://zenn.dev/sui_water/articles/502a6ef30b79e1/topics/gas)[![](https://storage.googleapis.com/zenn-user-upload/topics/3b81cfab49.png)Google Apps Script](https://zenn.dev/sui_water/articles/502a6ef30b79e1/topics/googleappscript)[![](https://zenn.dev/images/topic.png)clasp](https://zenn.dev/sui_water/articles/502a6ef30b79e1/topics/clasp)[![](https://static.zenn.studio/images/drawing/tech-icon.svg)tech](https://zenn.dev/sui_water/articles/502a6ef30b79e1/tech-or-idea)
 
## [](#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB) はじめに
 
2025年11月現在、Geminiを使用してGoogle Slidesを作成する「まじん式」プロンプトが話題です。  
<https://note.com/majin%5F108/n/n11fc2f2190e9>  
こちらの記事で初めて知りましたが、どうやらGoogle Apps Script(GAS)を使うと、社内限定で使えるWebアプリケーションを簡単に構築できるそうです。  
GAS自体は使ったことはありますが、Webアプリケーションを作ったことはありませんでした。  
本記事では、**実践的なサンプルプロジェクトとして**GoogleDriveにあるファイルを検索するアプリケーション(Drive検索ビューア)を構築していきます。  
開発にはclaspとTypeScriptを使用します。claspはCommand Line Apps Script Projectsの略称で、Apps Script プロジェクトをローカルで開発することができるOSSです。  
<https://github.com/google/clasp>  
また、従来のGASエディタではなく、使い慣れたVS Codeで開発を進めていきます。  
GASエディタでは困難だったClaude CodeなどのAIエージェントを組み合わせることで、モダンな開発環境でGAS製のWebアプリケーションを作成していきましょう。

取得自体はできましたが、絶妙に入ってほしくない箇所、例えば aside タグや footer タグの部分まで含まれてしまっているようです。

ぱっと思いついた範囲ですが、このような日々の業務に密着した課題を解決するアプリケーションを、自らの手で作り出すことが可能です。  
この記事が、皆さんの「あったらいいな」を実現するための一歩を踏み出すきっかけとなれば幸いです。  
以上になります✌️
 
## [](#%E5%8F%82%E8%80%83%E3%83%AA%E3%83%B3%E3%82%AF) 参考リンク
 
<https://github.com/google/clasp>  
<https://developers.google.com/apps-script/guides/clasp>  
<https://developers.google.com/identity/protocols/oauth2/scopes>
 
147
 
102
 
[![スイ](https://storage.googleapis.com/zenn-user-upload/avatar/c80d51cd90.jpeg)](https://zenn.dev/sui_water/articles/502a6ef30b79e1/sui%5Fwater)
 
[スイ](https://zenn.dev/sui_water/articles/502a6ef30b79e1/sui%5Fwater)
 
ソフトウェアエンジニアです。
 
### Discussion
 
![](https://static.zenn.studio/images/drawing/discussion.png)
 
[![スイ](https://storage.googleapis.com/zenn-user-upload/avatar/c80d51cd90.jpeg)](https://zenn.dev/sui_water/articles/502a6ef30b79e1/sui%5Fwater)
 
[スイ](https://zenn.dev/sui_water/articles/502a6ef30b79e1/sui%5Fwater)
 
ソフトウェアエンジニアです。
 
目次
 
1. [はじめに](#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB)  
   2. [この記事の対象読者](#%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%AE%E5%AF%BE%E8%B1%A1%E8%AA%AD%E8%80%85)
3. [構築するアプリの概要](#%E6%A7%8B%E7%AF%89%E3%81%99%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E6%A6%82%E8%A6%81)
4. [なぜclaspを使うのか](#%E3%81%AA%E3%81%9Cclasp%E3%82%92%E4%BD%BF%E3%81%86%E3%81%AE%E3%81%8B)
5. [開発環境のセットアップ](#%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)  
※中略

実際の記事として読む際には、aside タグや footer タグの部分は重要ではありません。Cloudflare では公式に API リファレンスを提供していますので、これらを取得対象から外すことができるか、公式ドキュメントを確認してみましょう。

developers.cloudflare.com

Page Not Found

Failed to fetch link preview.

addScriptTag プロパティの content に JavaScript を設定できます。不要な aside タグと footer タグを排除するスクリプトを入れて実行してみましょう。header タグの情報は入っていなかったのですが、他サイトで流用するときのために一応入れておきます。

 import Cloudflare from 'cloudflare';
 
 export default {
   async fetch(request, env, _ctx): Promise<Response> {
     const url = new URL(request.url);
     const targetUrl = url.searchParams.get('url');
 
     if (!targetUrl) {
       return new Response('URL parameter is required', { status: 400 });
     }
 
     const client = new Cloudflare({
       apiToken: env.CLOUDFLARE_API_TOKEN,
     });
     const markdown = await client.browserRendering.markdown.create({
       account_id: env.CLOUDFLARE_ACCOUNT_ID,
       url: targetUrl,
+      addScriptTag: [
+        {
+          content: `document.querySelectorAll('aside, header, footer').forEach(el => el.remove());`,
+        },
+      ],
     });
     return new Response(markdown, {
       status: 200,
       headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
     });
   },
 } satisfies ExportedHandler<Env>;

実行してみたところ、不要な部分が取得対象外になっていることが確認できます。

Zennの記事からasideタグとfooterタグが削除されたレスポンスの画像

さらに、Markdown 変換の精度を上げるために、不要なリソースの読み込みをブロックできます。rejectResourceTypesrejectRequestPattern パラメータを使うことで、CSS や画像ファイルなどのコンテンツに直接関係のないリソースを除外できます。

 const markdown = await client.browserRendering.markdown.create({
   account_id: env.CLOUDFLARE_ACCOUNT_ID,
   url: targetUrl,
+  rejectResourceTypes: ['stylesheet', 'image', 'media', 'font'],
+  rejectRequestPattern: ['/^.*\\.(css|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/'],
   addScriptTag: [
     {
       content: `document.querySelectorAll('aside, header, footer').forEach(el => el.remove());`,
     },
   ],
 });

rejectResourceTypes は、リソースタイプ単位でブロックします。スタイルシート、画像、メディア、フォントファイルなど、テキストコンテンツの抽出には不要なリソースを指定できます。rejectRequestPattern は正規表現パターンでリクエストをブロックします。拡張子ベースで細かく制御したい場合に便利です。
これらのパラメータを追加で使用することで、ページの読み込み速度が向上し、Markdown 変換の精度も上がります。

料金体系

Browser Rendering の料金体系は、使用方法によって異なります。

  • REST API - ブラウザの使用時間(Duration)のみで課金
  • Workers Bindings - 使用時間と同時実行数(Concurrency)の両方で課金

今回使用している REST API の場合、以下の料金プランになります。

プラン 無料枠 超過料金
Workers Free 1日10分 課金不可
Workers Paid 月10時間 $0.09/時間

例えば、Workers Paid プランで月に 50 時間使用した場合、計算は以下のようになります。

50時間 - 10時間(無料枠) = 40時間
40時間 × $0.09 = $3.60

REST API のレスポンスには X-Browser-Ms-Used ヘッダーが含まれており、そのリクエストで使用したブラウザ時間(ミリ秒単位)を確認できます。これを使って使用量をモニタリングすることも可能です。

まとめ

今回は、Cloudflare Browser Rendering を使って、URL を渡すと Markdown が返ってくる API を構築しました。REST API を使うことで、複雑なセットアップなしにシンプルな実装ができることを確認できました。

今後の展開としては、この記事の冒頭で触れたように、Workers AI と組み合わせて記事の要約機能を追加したり、Obsidian との連携を自動化したりといった拡張も考えられます。また、複雑なブラウザ操作が必要になった場合は、Puppeteer API への移行も選択肢になるでしょう。

Cloudflare Browser Rendering は、エッジでのブラウザ自動化という新しい可能性を提供してくれています。今回のようなシンプルなユースケースから始めて、徐々に活用の幅を広げていくのが良いのかなと思います。

今回使用したリポジトリは以下で確認できます。

github.com

GitHub - Suntory-Y-Water/browser-rendering-markdown

Contribute to Suntory-Y-Water/browser-rendering-markdown development by creating an account on GitHub.

参考

developers.cloudflare.com

Browser Rendering

Control headless browsers with Cloudflare's Workers Browser Rendering API. Automate tasks, take screenshots, convert pages to PDFs, and test web apps.

developers.cloudflare.com

REST API

The REST API is a RESTful interface that provides endpoints for common browser actions such as capturing screenshots, extracting HTML content, generating PDFs, and more. The following are the available options:

developers.cloudflare.com

Page Not Found

Failed to fetch link preview.

developers.cloudflare.com

Pricing

There are two ways to use Browser Rendering. Depending on the method you use, here is how billing works: