このコンテンツは自動機械翻訳サービスによる翻訳版であり、皆さまの便宜のために提供しています。原本の英語版と異なる誤り、省略、解釈の微妙な違いが含まれる場合があります。ご不明な点がある場合は、英語版原本をご確認ください。
本日、Workers Cacheをローンチします。これは、Workerの前に配置される階層型キャッシュで、1行のWrangler設定とすでにご存知のCache-Controlヘッダーで設定されます。
Workers Cacheを有効にすると、Workerへのキャッシュ可能なリクエストはすべて、最初にCloudflareのキャッシュにヒットします。新しいキャッシュされたレスポンスがある場合、Cloudflareは直接それを返します。Workerは実行されず、それに対するCPU時間も支払いません。欠落していた場合、Workerが実行され、レスポンスがキャッシュ可能な場合は、次のリクエストのためにCloudflareが保存します。地球上のどこからでも次のリクエストをキャッシュから直接提供することができます。
すべては1つの設定ブロックです。
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2026-05-01",
"cache": {
"enabled": true
}
}
その後、レスポンスにヘッダーを設定することで、HTTPが常に望んでいた方法でキャッシングを制御できます。
return new Response(body, {
headers: {
"Cache-Control": "public, max-age=300, stale-while-revalidate=3600",
"Cache-Tag": "products,product:123",
},
});
コンテンツが変更されると、Workerは自身のキャッシュを削除します。
await ctx.cache.purge({ tags: ["product:123"] });
これがAPI全体です。ゾーンの設定も、ルールエンジンのセットアップも、プロビジョニングすべき個別のキャッシュも、2つ目の製品にログインする必要もありません。Workerのコードは設定サーフェスであり、キャッシュはWorkerがどこにいても追跡します。バニティドメイン上では、workers.dev上で、Workers for Platformsテナントのサービスバインディングの背後にいるものです(プレビュー)。1つのWorker、1つのキャッシュを、一度設定すればよいのです。
それが対応面積に影響を与えますその下には多くの機能があります。ネットワーク全体での階層型キャッシング、stale-while-revalidateの完全サポートにより古い応答がユーザーをブロックしないこと、Varyを介したコンテンツネゴシエーション、ctx.propsを介したマルチテナント安全なキャッシュキー、タグやパスプレフィックスによるプログラム的パージ、そして当社が最大のロック解除と考える部分は、パブリックなものだけでなく、すべてのWorkerエントリポイントの前面に位置するキャッシュで、エントリーポイントごとにキャッシュするものとしないものを制御できます。最後の部分は、アプリの構造に直接キャッシングを構成できることを意味します。キャッシュステージは、両側のコードによって設定された場所に割り当てられた一連のエントリーポイントです。以下ですべて説明します。
Workers Cacheは、Wranglerで有効化されており、プランにかかわらず全Workerでご利用いただけます。
これは、当社がWorkersにずっと望んでいたキャッシュAPIです。これまでにかかった理由、そのおかげで可能になること、そして次に来るものを以下にまとめます。
サーバーレンダリングアプリの最前線にキャッシュが必要な理由
2017年にWorkersを導入した際、Cloudflareのネットワーク上でコードを実行し、オリジンに到達する途中でリクエストを変換できるというものでした。Workerはキャッシュとオリジンの前に位置し:
これは、当社がターゲットとしていたユースケースに適切なモデルでした。すべてのリクエストにヘッダーを追加し、URLを書き換え、A/Bスプリットを行い、またはオリジンに到達する前にトラフィックをフィルタリングしたい場合、Workerをキャッシュの前に配置して、オリジンはキャッシュされたものを完全に制御できるようになりましたそして、そうでなかったものについて。お客様は素晴らしいものを構築しました。
しかし、世界は変わりました。Workersは、オリジンに後付けするものではなく、まさにオリジンそのものになり始めたのです。Astro、TanStack Start、Next.jsのようなフレームワーク、Remix、SvelteKitにはすべて、アプリをWorkerとして構築するCloudflareアダプターが搭載されています。それらにオリジンはありません。Workerはサーバーです。
Workerがオリジンである場合、オリジナルのアーキテクチャにはキャッシュするものはありません。すべてのリクエストは、レスポンスが1秒前に返したものとバイトごとに同じであっても、コードを実行します。Workersのランタイムは、これが機能するのに十分な速さです — 毎秒数千万のリクエストを難なく処理しますが、「すべてのリクエストをレンダリングするのに十分な速さ」であっても、すべてのページ読み込みで遅延が発生し、すべての呼び出しでCPU時間が消費されます。そして、サーバーレンダリングアプリでは、すべてのページ読み込みは定義上レンダリングです。
Workers キャッシュは、アーキテクチャを覆します。CloudflareのキャッシュはWorkerの前に配置されます:
キャッシュヒットした場合、Workerはまったく実行されません。Cloudflareはキャッシュされたレスポンスを返します。CPU課金はゼロのままです。欠落していた場合、Workerは1回実行され、キャッシュを作成します。そして、どこからでも、次のリクエストはコードを呼び出すことなくキャッシュから提供されます。
これは、Workersでのサーバーサイドレンダリングに欠けていたものです。以前は、満足できない選択肢があります。
構築時にすべてをプリレンダリングします(「静的サイト生成」)。ページの読み込みは高速ですが、変更はすべて完全な再構築と再デプロイが必要です。数千ページのドキュメントサイトであれば、5~10分です。大規模なeコマースサイトでは、さらに深刻です。そして、何かに触れるたびにビルドが実行されてしまいます。
すべてのリクエストですべてのページをレンダリングします。最新のコンテンツですが、ページ読み込みごとにレンダリングコストがかかり、すべての訪問者が遅延を支払います。
Workers Cacheでは、3番目のオプションとして、オンデマンドのサーバーレンダリング、レンダリングされたレスポンスのキャッシュ、選択した有効期限(TTL)(有効期限(TTL))で更新することができます。新しいページへの最初のリクエストは、まだレンダリングされます。後続のリクエストは、キャッシュが有効期限が切れるまで、ページが静的であるかのように提供されます。キャッシュの有効期限が切れると、次のリクエストは再レンダリングをトリガーします。stale-while-revalidateなら、そのリクエストも待ちません。
ビルド時間なしで静的サイトのスピードを手に入れ、コストなしでサーバーレンダリングの新鮮さを実現できます。増分静的再生のようなフレームワーク固有のマシンはありません。動作は、オリジンとなるように設計されたコードの前で、設計された方法で動作するHTTPキャッシングだけです。
stale-while-revalidateは、瞬時に感じられる部分です。
stale-while-revalidateディレクティブでは、キャッシュされたレスポンスの有効期限が切れた時に、バックグラウンドでレスポンスを更新しながら、古いコピーを即座に提供することを許可するという指示をCloudflareに出します。Cloudflareは今年の初めにstale-while-revalidateの完全サポートをリリースしました。これは、「Workerをキャッシュします」を「Workerのサイトが静的に感じられる」ように変えるディレクティブです。
これがないと、キャッシュエントリの期限が切れた後の最初のリクエストは、Workerがページをゼロからレンダリングするのを待たなければなりません。ユーザーはその遅延を認識します。これにより、有効期限後の最初のリクエストは、古いページをすぐに取得し(Cf-Cache-Status: UPDATING ヘッダーとともに)、Workerはバックグラウンドで実行されてキャッシュを更新します。リフレッシュをトリガーしたユーザーを含むすべてのユーザーが、キャッシュスピードのレスポンスを得ることができます。
実際には次のようになります:
export default {
async fetch(request) {
const html = await renderPage(request);
return new Response(html, {
headers: {
"Content-Type": "text/html; charset=utf-8",
// Treat as fresh for 5 minutes; serve stale for up to an hour
// while a background refresh runs.
"Cache-Control": "public, max-age=300, stale-while-revalidate=3600",
},
});
},
};
このクリックを行うためのメンタルモデル:
フレッシュウィンドウ (max-age): Cloudflareはキャッシュされたレスポンスを提供します。Workerが動作しません。
古いウィンドウ (stale-while-revalidate): Cloudflareはキャッシュされたレスポンスを提供します。Workerがバックグラウンドで実行されて更新されます。待つユーザーはいません。
両方のウィンドウの外側:CloudflareはWorkerを実行して新しいレスポンスを生成し、ユーザーはそのレンダリングを待ちます。
ウインドウを選択します。数分ごとに更新される製品カタログの場合、max-age=300、stale-while-revalidate=3600は、訪問者が基本的に待つことを意味せず、Workerはコンテンツの鮮度を保つのに十分な頻度で実行されます。ほとんど変更されていないブログアーカイブの場合、max-age=86400、stale-while-revalidate=2592000は、Workerがページあたり1日に1回実行されることを意味します。
新規ページの最初のリクエストは、すべてのレンダリングコストが発生する唯一のリクエストです。その後、ページは訪問者にとって静的な出力のように動作しますが、Workerはまだページの生成方法を所有します。
実際のアプリがすべてのクライアントに同じバイトを返すことは、稀なことです。同じ製品ページがブラウザ用のHTMLで、APIクライアント用のJSONである場合もあります。同じ画像が、サポートしているクライアントではWebP、そうでないクライアントではJPEGなどです。同じホームページが、ユーザーによって英語、フランス語、または日本語で表示される場合もあります。
キャッシュなしでこれを行うのは簡単です。Workerはリクエストヘッダーを読み込んで、正しいものを返すだけです。キャッシュでやると、たいてい不格好になります。ほとんどのキャッシュは、複数の表現を持つURLでは何もキャッシュしないか、1つの表現をキャッシュしてすべてのユーザーに提供するという2つの悪い選択肢があります。
Workers Cacheは、標準のHTTP Vary ヘッダーをサポートしており、これを解決する正しい方法です。WorkerがVary: Accept-Encoding(またはAccept、またはAccept-Language、あるいはその他のリクエストヘッダー)を含むレスポンスを返すと、Cloudflareはこれらのヘッダーの異なる組み合わせごとに個別にキャッシュされたバリアントを保存し、保存された値が受信リクエストと一致するバリアントだけを返します。
export default {
async fetch(request) {
const accept = request.headers.get("Accept") ?? "";
const wantsWebp = accept.includes("image/webp");
const body = wantsWebp ? await fetchWebpImage() : await fetchJpegImage();
return new Response(body, {
headers: {
"Content-Type": wantsWebp ? "image/webp" : "image/jpeg",
"Cache-Control": "public, max-age=3600",
// Cache a separate variant per distinct Accept header value.
Vary: "Accept",
},
});
},
};
1つのURL、2つのキャッシュされたバリアント。Accept: image/webp,*/*を送信するブラウザは、WebPを取得します。Accept: image/jpegを送信するブラウザは、JPEGを取得します。どちらもキャッシュから来ています。Workerは、それぞれへの最初のリクエストで両方のバリアントを書き込み、その後はどちらもゼロ回実行します。
これは、コンテンツネゴシエーションのためのよく普及しているHTTP標準であり、Workers キャッシュはRFC 9110およびRFC 9111に記載されている方法で実装しています。どのヘッダーに基づいてVaryできるかについての許可リストは存在しません。必要なものをすべてリストアップし、Cloudflareがその逐語値のバリアントをキー入力します。ドキュメントでは、エッジケース(ゲートウェイWorkerでヘッダーを正規化することでバリアントのファンアウトを制御する方法、パージがURLのすべてのバリアントをまとめて無効化する理由、そしてキャッシュを完全に無効にする1つのケース(Vary: *))について解説しています。
これはWorkerのキャッシュであり、ゾーンのキャッシュではありません
これらすべてによって何が可能になるか、その前に、名前に値する概念的な変化があります。
Cloudflareには永遠にキャッシュがあります。ゾーンレベルで設定されます:Cache Rules、Page Rules、キャッシュされたファイル拡張子リスト、Cache Reserve、階層型キャッシングトポロジー、カスタムキャッシュキー。すべてはゾーンごとに設定されており、従来、Workerはそのゾーンの設定に適合するか、それを回避する必要がありました。
Workers キャッシュは違います。これはWorkerのキャッシュです。ゾーンではなく、Workerに属します。これは、重大な結果をもたらします。
ゾーン構成の管理は不要です。キャッシュルール、キャッシュレベル設定、ファイル拡張子リスト、Page Rulesは、Workers キャッシュにはいずれも適用されません。WorkerのCache-Controlヘッダーがその設定です。
キャッシュはホスト名ではなく、Workerに従います。api.example.comにバインドされているWorker、api.example.net、サービスバインディングは、3つのすべてで1つのキャッシュを共有します。/users/42へのリクエストは、どの方法で来たかにかかわらず、同じキャッシュされたエントリにヒットします。
キャッシュはworkers.dev上で機能します。これはプレビューURLで機能します(各プレビューは独自のキャッシュを取得するため、変更をテストしても本番環境に影響を与えることはありません)。Workers for Platformsで動作します(各ユーザーWorkerは、ディスパッチ担当者や他のテナントから分離された独自のキャッシュを持ちます)。これらはすべて、キャッシュに関しては二等級の市民でした。しかし、もはやそうではありません。
パージの範囲は、Workerのエントリポイントに設定されます。ctx.cache.purge({ purgeEverything: true })を呼び出すと、Workerエントリポイントのキャッシュを削除するだけです。ゾーンの他のコンテンツを削除するリスクはありません。あるWorkerのデプロイが別のWorkerのデータを無効にするリスクがありません。
キャッシングに関する設定は、コードで設定されます。どのパスがTTLを長くするか(パス上で分岐して、異なるmax-ageを設定する)、どのリクエストがキャッシュをバイパスするか(Cache-Control: privateを返す)、キャッシュキーの形成方法など(ctx.propsに入るものがコントロールでき、ディスパッチする前に、Gateway WorkerでURLを正規化する)などです。既に書き込んだWorkerが設定サーフェスです。
ドキュメント全体では、Workers Cache: Workerのキャッシュでこれについて詳しく説明しています。
Workers Cacheは、既定で地域別に階層化されています。2つのレイヤーがあります:
リクエストは、最初に下位層にヒットします。ヒットすれば、レスポンスが提供され、それは終わりです。欠落していた場合、下位層は上位層に要求します。攻撃を受けた場合、レスポンスは返され、途中で下位層に保存されます。両方の階層がミスした場合のみ、Workerは実際に実行され、その実行からのレスポンスは両方の階層に保存されます。
これが重要な理由は、世界中で最初のリクエストによって上位層にデータが入力されるためです。どのデータセンターからでも、Workerを実行せずに、上位層から処理できます。そのデータセンターの下位層が以前そのリクエストを見たことがなくても、キャッシュヒット率は、単一のフラットなキャッシュレイヤーを使用する場合よりも劇的に高くなります。これは、Workerがオリジンである場合に必要なものです。
これは、現在ゾーンでTiered キャッシュを支えるトポロジーと同じですが、設定する必要はありません。「Workerの階層型キャッシュをオンにする」ためのダイアログはありません。キャッシングを有効にしているWorkerは、すべて無料で階層化を利用できます。
If your Worker uses Smart Placement, the キャッシュ composes cleanly with it: tiers are consulted first, and only if both miss does Smart Placement route execution close to your origin.ドキュメントでは、これらのレイヤーの相互作用について、さらに詳しく説明しています。これには、今後改善予定のいくつかの課題も含まれます。
Webパフォーマンスには、誰も完全に解決していない緊張が広がります。コードはユーザーの近くで実行されたい(ユーザーとサーバーの間の往復は重要なパス上にあるため)し、コードをユーザーの近くで実行したいのです。データを保護する方法です(すべてのデータベースクエリーもラウンドトリップであるため)。1つを選んでも、もう1つは遅くなります。
私たちはその両方を長年追い求めてきました。当社のネットワークを利用すれば、世界のインターネットユーザーのおよそ95%が約50ミリ秒以内で到達可能になります。Smart Placement(スマートプレイスメント)とPlacement Hints(プレイスメントヒント)を使用すると、クラウドリージョンについて考える必要なく、コードをデータの近くに置くことができます。しかし、これまでは、2つの部分は完全に構成されていませんでした。「ユーザーの近く」または「データの近く」でアプリの両方を適切な場所に同時に配置したい場合は、Cloudflareの専門家になる必要がありました。もっとうまいやり方があるはずだと思いました。
Workers キャッシュは、そのギャップを埋めるツールです。キャッシュはWorker(ゾーンではなく)に属し、Workers間のサービスバインディングとctx.exportsの呼び出しはキャッシュを通過するため、アプリをWorkersの連鎖として構築できます。各Workerは実行すべき場所で実行され、キャッシュがそれらの間のつなぎ目となります。
アーキテクチャは次のようになります:
Worker Aはユーザーの近くで実行されます。認証、レート制限、ルーティング、ヘッダーの正規化、データに依存しないHTMLページの外側の「シェル」のレンダリングなど、すべてのリクエストの安価で遅延に影響を受けやすい部分を処理します。
Worker Bは、Smart Placement または明示的な Placement Hint のおかげで、データの近くで実行されます。データを取得するページのサーバーレンダリング、製品カタログの読み取り、検索結果の生成、APIの集約、コストのかかる変換など、大変な作業です。
Workers CacheはWorker Bの前に位置します。Worker Aがサービスバインディングを介してWorker Bを呼び出すと、CloudflareはWorker Bのキャッシュを最初にチェックします。ヒットすると、Worker Aがレスポンスを受け取り、Worker Bはまったく実行されません。データセンターのホップも、データベースクエリーも、レンダリング作業もありません。
キャッシュヒット経路は、ユーザー→ユーザーの近くのWorker A → Worker Bのキャッシュヒット → レスポンスとなります。データホップは、欠落していた場合にのみ支払われます。ホットページはユーザーの前面にコードを配置する速度で実行され、コールドページは実行時にデータの近くで実行されるため、恩恵を受けることができます。
特別な設計は必要ありません。アプリを2つのWorkersとして記述し、一方をサービスバインディングで向け、Worker Bのwrangler.jsoncファイルでキャッシュをオンにして、完了です。
デフォルトでマルチテナント、ctx.props付き
ユーザー固有のデータを返すWorkerをキャッシュする場合。たとえば、ログインしているユーザーごとに異なるコンテンツを提供するAPIは、あるユーザーが別のユーザーのキャッシュされた応答を見ることができないようにする方法が必要になります。標準的なソリューションは、「認証されたリクエストをキャッシュしない」ことであり、Cloudflareの自動バイパスがまさにそれを実現します。しかし、「何もキャッシュしない」とでは、パフォーマンスの向上全体が妨げられます。
Workers Cacheは、呼び出し元のctx.propsをキャッシュキーの一部にすることで、これを解決します。あるWorkerがサービスバインディングを介して別のWorkerを呼び出し、ユーザーID、テナントID、またはその他の識別子とともにctx.propsを渡すと、異なるpropsを持つ呼び出し元は別々のキャッシュエントリを取得します。あるユーザーのレスポンスが、別のユーザーのキャッシュに漏洩することはありえません。
import { WorkerEntrypoint } from "cloudflare:workers";
interface Props { userId: string; }
export default class Backend extends WorkerEntrypoint<Env, Props> {
async fetch(request: Request): Promise<Response> {
// ctx.props.userId is part of the cache key. User A and User B
// requesting the same URL get separate cached entries.
const { userId } = this.ctx.props;
const data = await loadUserData(userId);
return new Response(JSON.stringify(data), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=300",
},
});
}
}
典型的なパターンは、Gateway Workerでリクエストを認証し、Authorizationヘッダーを削除し、認証済みユーザーのIDをctx.propsに設定し、キャッシュされたバックエンドWorkerを呼び出すことです。ゲートウェイはすべてのリクエスト(認証が必要)に対して実行されますが、高価なバックエンドは、そのユーザーのキャッシュエントリがまだない場合にのみ実行されます。認証済みAPIは、「キャッシュできない」ものから「完全に安全にユーザーごとにキャッシュされる」ものになり、キャッシュキーが分離を行います。ドキュメントでは、ctx.propsを使用したマルチテナントの安全性と、ユーザーごとの認証済み応答の例で、これについて詳しく説明しています。
他のCDNは、正確性とヒット率のどちらかを選択するようにします。各ユーザーのトークンでキャッシュをキー設定するか、すべてのリクエストをオリジンに戻して承認するかです。Workers Cacheを使用すると、リクエストごとの承認境界を維持しながら、キャッシュされたAPI応答をエッジで共有することができます。私たちは、認証済みのマルチテナントAPIのビルトインモデルとしてこれを提供するCDNを他に知りません。当社はそれをかなり誇りに思っています。
すべてのWorkerエントリポイント間のキャッシュ
ここは、Workers キャッシュで最大のアンロックと考えている部分であり、「Workersの前で動作するCDNキャッシュ」と考える人には、見つけるのが最も難しい部分です。
Workers キャッシュは、すべてのWorkerエントリポイントの前に配置されます — デフォルトのエクスポート、すべての名前付きWorkerEntrypoint、およびctx.exportsを介した、同じWorker内のエントリポイント間のすべての呼び出しです。最後の文言は、構築可能なものを変えるものです。
あるエントリポイントがctx.exportsを介して別のエントリポイントを呼び出すと、キャッシュはブラウザからのリクエストを評価するのと同じ方法でその呼び出しを評価します。ヒットすると、キャッシュされたレスポンスが返され、呼び出し対象は実行されません。ミスが発生すると、呼び出し先が実行され、その応答が独自のキャッシュキーの下に保存されます。キーは、呼び出し先のエントリーポイント、パス、クエリ文字列、およびctx.propsによってキー付けされます。呼び出し元はすべてのリクエストに対して実行されますが、呼び出し元に手渡されるものはすべて個別にメモされます。
エントリポイントごとに、どのものをキャッシュするかを決定します。Wrangler設定では、exportsマップを使用して、各エントリーポイントのキャッシュを名前でオンまたはオフにすることができます("default"がデフォルトのエクスポートです)。エントリポイントをオプトインして、生成するレスポンスをキャッシュします。すべてのリクエストで実行し続けることができます。ゲートウェイやルーターのエントリポイント(認証、正規化、ディスパッチを行うもの)は、常に実行され、自らの出力がキャッシュから提供されることはありません。
これは、プリミティブを作ることができます。認証、正規化、ルーティング、高価な読み取り、データレイヤーなど、小さなエントリーポイントの連鎖としてWorkerを作成でき、Workers Cacheを好きな場所にキャッシュすることができます。キャッシュされた各エントリポイントは、独自のキー、独自の有効期限(TTL)、パージのための独自のタグネームスペースを持つメモ化の単位です。キャッシングに関して設定したいことすべて(いつ実行するか、何をキーにするか、いつ無効化するか)は、通常のWorkerコードとして表現されます。呼び出すエントリポイント、転送するリクエスト、渡すctx.props、設定するCache-Controlなどです。
これを具体的にするために、ここでは他のプラットフォームでは簡単に実行できなかった3つのことを行う単一のWorkerを紹介します。すべてのリクエストを認証し、高価なバックエンドをマルチテナント安全なキャッシュキーの背後にキャッシュし、データが収集されたときにそのキャッシュを無効化します変化するものです。
キャッシングはエントリーポイントごとに設定されます。ゲートウェイはすべてのリクエストに対して実行する必要があります。認証のために、キャッシュされたゲートウェイレスポンスはその認証チェックをスキップするため、デフォルトのエントリポイントのキャッシュを無効にし、内側のエントリポイントだけを有効にします。
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2026-05-01",
"cache": { "enabled": true },
"exports": {
// The gateway runs on every request — don't cache it.
"default": { "type": "worker", "cache": { "enabled": false } },
// Cache the expensive inner entrypoint.
"CachedBackend": { "type": "worker", "cache": { "enabled": true } }
}
}
import { WorkerEntrypoint } from "cloudflare:workers";
interface Env { API_TOKEN: string; }
interface Props { userId: string; }
// Inner entrypoint: the expensive work. Workers Cache sits in front
// of this — on a hit, this code never runs.
export class CachedBackend extends WorkerEntrypoint<Env, Props> {
async fetch(request: Request): Promise<Response> {
// ctx.props.userId is part of the cache key, so this is cached
// separately for every user.
const { userId } = this.ctx.props;
const data = await loadExpensiveData(userId);
return new Response(JSON.stringify(data), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=300, stale-while-revalidate=3600",
"Cache-Tag": `user:${userId}`,
},
});
}
// Invalidate a user's cached response. purge() is scoped to the
// entrypoint that calls it, so it must run inside CachedBackend —
// the entrypoint that owns the cached response.
async invalidate(userId: string): Promise<void> {
await this.ctx.cache.purge({ tags: [`user:${userId}`] });
}
}
// Outer entrypoint: runs on every request to authenticate and route.
// Caching is disabled for it in Wrangler config (above), so it always
// runs and the auth check is never skipped by a cache hit.
export default {
async fetch(request, env, ctx): Promise<Response> {
const userId = await authenticate(request, env);
if (!userId) return new Response("Unauthorized", { status: 401 });
// Invalidate this user's cache on writes, from the entrypoint that
// owns it.
if (request.method === "POST") {
await handleWrite(request, userId);
await ctx.exports.CachedBackend.invalidate(userId);
return new Response("OK");
}
// For reads: strip Authorization (otherwise Cloudflare's automatic
// bypass fires and nothing caches), then dispatch to the cached
// backend with the authenticated user's identity in ctx.props.
const forwarded = new Request(request);
forwarded.headers.delete("Authorization");
return ctx.exports.CachedBackend.fetch(forwarded, {
props: { userId },
});
},
} satisfies ExportedHandler<Env>;
すべては1つのWorkerです。1つのソースファイル。一度のデプロイ。しかし、2つの実行段階があります。キャッシュは、1つの小さなexportsブロックで、ゲートウェイのキャッシュをオフにし、バックエンドのキャッシュをオンにします。そして、キャッシュは、ユーザーごとにキー設定され、書き込みパスによって無効化され、バックエンドのリフレッシュ中に古くなったものを提供します。キャッシュステージは後付けのものではありません。これはプログラムのレイヤーであり、コードで記述されています。
これが構成するパターンはオープンエンドです。同じ形が以下でも機能します。
Durable Objectをキャッシングする。Durable Objectをエントリーポイントの背後にラップし、レスポンスにCache-Controlを設定すると、ヒットした読み取りはDurable Objectにアクセスしなくなります。書き込みは直接DOに送られ、タグによるキャッシュをパージします。DOは、キャッシングが行われていることを知りません。
Varyの前にAccept-Encodingを正規化します。外側のエントリポイントは、request.cf.clientAcceptEncodingから元のエンコーディングを復元し(Cloudflareのフロントラインは、キャッシュ効率のためにそれを正規化します)、実際の値が異なるキャッシュされたエントリポイントに転送します。ヒット率は高い水準で維持クライアントは正しいエンコーディングを取得します。
キャッシュする前に追跡パラメータを削除します。外側のエントリポイントはURLを正規化します。または、カスタムキャッシュキーをcf.cacheKeyとともにctx.exports呼び出しで設定するため、キャッシュされた内部エントリポイントは正規の形式のみを参照し、?utm_source=anythingは単一のキャッシュエントリに集約されます。
積み上げましょう。1つのWorkerは、認証とルーティングを行う外部エントリポイント、追跡パラメータを削除してエンコーディングヘッダーを復元する正規化エントリポイント、Durable Objectの前のキャッシュされたエントリポイント、認証されていないパブリックAPIのための別のキャッシュされたエントリポイント、それぞれがキャッシュによって接続されていますどこに配置するかを決めただけです。ドキュメントの例のページでは、これらのいくつかをエンドツーエンドで説明しています。
これができる他のプラットフォームはありません。CDNキャッシュは、オリジンの前にあります。関数プラットフォームは機能を実行します。私たちは、単一のデプロイ可能なユニット内で、アプリケーションの各部分の間に位置し、各キャッシュステージはそのどちら側のコードによって構成されたキャッシュを提供する他のプラットフォームを知りません。それがWorkers キャッシュです。また、Smart Placement、Durable Objects、サービスバインディング、ctx.props、ctx.exportsなど、このプラットフォームがすでに提供するあらゆるものと構成されているため、構築できるパターンは無限です。この記事では、まだ表面的なものにとどまっています。
Astroで構築している場合、CloudflareアダプターがWorkers キャッシュを転送します。設定にCacheCloudflareプロバイダーを追加するだけです:
// astro.config.mjs
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import { cacheCloudflare } from "@astrojs/cloudflare/cache";
export default defineConfig({
adapter: cloudflare(),
output: "server",
experimental: {
cache: { provider: cacheCloudflare() },
routeRules: {
"/products/*": { maxAge: 300, swr: 3600, tags: ["products"] },
"/blog/*": { maxAge: 60, swr: 86400, tags: ["blog"] },
},
},
});
アダプターはキャッシュを有効にし、Astroが生成するレスポンスに正しいヘッダーを設定し、無効化のためにCache-Tag値を付加し、cache.invalidate()を提供します。コンテンツ変更時のタグのパージヘルパー。サーバーレンダリングを選択したAstroページでは、上記の「一度レンダリング、キャッシュ、バックグラウンドで更新」のフローが自動的に適用されます。ルートごとの設定は不要で、フレームワーク固有のランタイムレイヤーを学習する必要もありません。
他のフレームワークの保守管理者と協力して、同じ統合を出荷しています。Cloudflare用のフレームワークアダプターを構築すれば、Workers キャッシュ APIsはまさに希望通りです。ヘッダー主導の設定、プログラムによるパージ、プラットフォーム固有の概念をモデル化する必要はありません。
Workerと同じダッシュボードでキャッシュを表示
キャッシングは、それが行っていることを把握できている場合にのみ有効です。Workers Observability ダッシュボードで、呼び出しごとのキャッシュヒット情報が表示されるようになりました。
Workerごとに以下を見ることができます:
キャッシュヒット率の推移。キャッシュを有効にした後に上昇したい数値です。
ヒット、ミス、更新、バイパスの内訳。ヒット率が低い場合、ここで原因がわかります。 BYPASSレスポンスが多すぎる(何かがCookieを設定しているから?)、 MISSレスポンスが多すぎる(キャッシュキーが思ったより分割されているから?)、 UPDATINGレスポンスが多すぎる( max-ageがトラフィック間隔より短いため?)
たとえば、ログ、例外、CPU時間、リクエストカウントなど、Workerの他の可観測性と同じダッシュボード上にあるため、ゾーンとWorkerを見てコンテキストを切り替える必要はありません。何が起こっているかを理解するために、ゾーンとWorkerを見てください。
キャッシュヒットはWorkerを実行せず、CPU時間にも課金しません。これらは、他の呼び出しと同じように、標準的なWorkersリクエストレートでリクエストとしてカウントされます。キャッシュミスやバイパスによる通常課金は、キャッシュしない場合と同じで、リクエスト+CPU時間です。
結果 | リクエスト料金 | CPU時間の課金 |
キャッシュHIT(Workerは実行されない) | 標準レート | 請求なし |
キャッシュMISS (Worker runs) | 標準レート | 請求対象 |
キャッシュBYPASS(Worker実行) | 標準レート | 請求対象 |
静的アセットリクエスト | 標準レート | 請求なし |
WorkerからWorkerへの呼び出し | 標準レート | Workerが実行された場合に課金 |
個別のWorkers Cache SKUや、GBごとのキャッシュストレージ料金はありません。階層型キャッシング、パージ、stale-while-revalidate、および上記の分析がすべて含まれています。リクエストがWorkerを実行し、Workers キャッシュがヒットとしてそれを提供した場合、まだ標準リクエストレートを支払いますが、そのリクエストにCPU時間は支払われません。このため、そのキャッシュヒットは、Workerで同じレスポンスをレンダリングするよりも低コストになります。
注意点として、キャッシュが有効な場合、通常は無料であるリクエスト(静的アセットへのリクエストや、サービスバインディングまたはctx.exportsを介した Worker 間での呼び出し)についても、標準のリクエスト料金が課金されるようになります。これは、それらのリクエストがすべて、Worker の手前にあるキャッシュを参照するようになるためです。
次に当社が取り組みたいと考えていること:
Smart Placement で、よりスマートなコロケーションを実現します。現在、Cloudflareは、上位層のキャッシュとSmart Placementのターゲットを別々に選択しています。完全に見逃された場合、リクエストはCloudflareロケーション間を2回移動します。1 回は上位層をチェックするため、そして再度、そのデータの近くでWorkerを実行するためです。当社はその選択肢を調整しているため、見逃した場合も長距離移動は一度だけです。
レスポンスサイズ制限が大きい。起動時は、アカウントに関係なく、すべてのレスポンスがFreeプランのキャッシュ可能なサイズ制限(512 MB)に従います。これは一時的なものです。いくつかのロールアウト手順が完了すると、標準的なプランごとのキャッシュ制限が適用されます。
より多くのフレームワーク統合。Astroには、Workers キャッシュとの統合機能が組み込まれています。弊社は保守管理者と協力して、TanStack StartやVinext経由のNext.js など、他のフレームワークにも同様の統合を追加しています。
キャッシュされた応答を古いとマークするAPI。ctx.cache.purge()は、一致するレスポンスをキャッシュから削除します。ここでは、ctx.cache.invalidate() APIについて検討しています。この API を使用すると、一致するレスポンスが有効期限切れとして扱われるため、ワーカーがバックグラウンドでキャッシュを更新している間も、次のリクエストでは「stale-while-revalidate」フラグ付きの古いレスポンスを高速に取得できるようになります。
Workers キャッシュは、現在からプランにかかわらずすべてのWorkerでご利用いただけます。
まず、「cache」: { "enabled": true }をwrangler.jsoncに追加し、再デプロイして、Cache-Controlヘッダーの設定を開始してください。Workers Cacheのドキュメントでは、クイックスタート、キャッシュキー、パージ、構成パターンと例、デバッグなど、すべての機能について説明しています。
Workersはキャッシュの前で実行されていました。現在は、その裏で実行できるようになっています。必要なだけ、またはサービスバインディングとの両方を一度に使用できます。
皆さんがどんなものを構築されるのか、楽しみにしています。