フェッチを使用したキャッシュ
リソースをキャッシュする方法を、TTL、カスタムキャッシュキー、およびフェッチリクエストのキャッシュヘッダーを設定することで決定します。
export default { async fetch(request) { const url = new URL(request.url); // キャッシュキーにはパスのみを使用し、クエリ文字列を削除します // そして常にHTTPSを使用して保存します。例えば、https://www.example.com/file-uri-here const someCustomKey = `https://${url.hostname}${url.pathname}`; let response = await fetch(request, { cf: { // コンテンツタイプに関係なく、このフェッチを常にキャッシュします // リソースを再検証する前に最大5秒間 cacheTtl: 5, cacheEverything: true, // エンタープライズ専用機能、他のプランについてはCache APIを参照してください cacheKey: someCustomKey, }, }); // Responseオブジェクトを再構築して、そのヘッダーを変更可能にします。 response = new Response(response.body, response); // ブラウザで25分間キャッシュするためにキャッシュコントロールヘッダーを設定します response.headers.set("Cache-Control", "max-age=1500"); return response; },};export default { async fetch(request): Promise<Response> { const url = new URL(request.url); // キャッシュキーにはパスのみを使用し、クエリ文字列を削除します // そして常にHTTPSを使用して保存します。例えば、https://www.example.com/file-uri-here const someCustomKey = `https://${url.hostname}${url.pathname}`; let response = await fetch(request, { cf: { // コンテンツタイプに関係なく、このフェッチを常にキャッシュします // リソースを再検証する前に最大5秒間 cacheTtl: 5, cacheEverything: true, // エンタープライズ専用機能、他のプランについてはCache APIを参照してください cacheKey: someCustomKey, }, }); // Responseオブジェクトを再構築して、そのヘッダーを変更可能にします。 response = new Response(response.body, response); // ブラウザで25分間キャッシュするためにキャッシュコントロールヘッダーを設定します response.headers.set("Cache-Control", "max-age=1500"); return response; },} satisfies ExportedHandler;from pyodide.ffi import to_js as _to_jsfrom js import Response, URL, Object, fetch
def to_js(x): return _to_js(x, dict_converter=Object.fromEntries)
async def on_fetch(request): url = URL.new(request.url)
# キャッシュキーにはパスのみを使用し、クエリ文字列を削除します # そして常にHTTPSを使用して保存します。例えば、https://www.example.com/file-uri-here some_custom_key = f"https://{url.hostname}{url.pathname}"
response = await fetch( request, cf=to_js({ # コンテンツタイプに関係なく、このフェッチを常にキャッシュします # リソースを再検証する前に最大5秒間 "cacheTtl": 5, "cacheEverything": True, # エンタープライズ専用機能、他のプランについてはCache APIを参照してください "cacheKey": some_custom_key, }), )
# Responseオブジェクトを再構築して、そのヘッダーを変更可能にします response = Response.new(response.body, response)
# ブラウザで25分間キャッシュするためにキャッシュコントロールヘッダーを設定します response.headers["Cache-Control"] = "max-age=1500"
return responseuse worker::*;
#[event(fetch)]async fn fetch(req: Request, _env: Env, _ctx: Context) -> Result<Response> { let url = req.url()?;
// キャッシュキーにはパスのみを使用し、クエリ文字列を削除します // そして常にHTTPSを使用して保存します。例えば、https://www.example.com/file-uri-here let custom_key = format!( "https://{host}{path}", host = url.host_str().unwrap(), path = url.path() );
let request = Request::new_with_init( url.as_str(), &RequestInit { headers: req.headers().clone(), method: req.method(), cf: CfProperties { // コンテンツタイプに関係なく、このフェッチを常にキャッシュします // リソースを再検証する前に最大5秒間 cache_ttl: Some(5), cache_everything: Some(true), // エンタープライズ専用機能、他のプランについてはCache APIを参照してください cache_key: Some(custom_key), ..CfProperties::default() }, ..RequestInit::default() }, )?;
let mut response = Fetch::Request(request).send().await?;
// ブラウザで25分間キャッシュするためにキャッシュコントロールヘッダーを設定します let _ = response.headers_mut().set("Cache-Control", "max-age=1500"); Ok(response)}// Cloudflareにアセットをキャッシュさせるfetch(event.request, { cf: { cacheEverything: true } });キャッシュレベルをCache Everythingに設定すると、アセットのデフォルトのキャッシュ可能性が上書きされます。TTLについては、Cloudflareは依然としてオリジンによって設定されたヘッダーに依存します。
リクエストのキャッシュキーは、キャッシュ目的で2つのリクエストが同じかどうかを決定します。リクエストが以前のリクエストと同じキャッシュキーを持っている場合、Cloudflareは両方に対して同じキャッシュされたレスポンスを提供できます。キャッシュキーの詳細については、カスタムキャッシュキーの作成ドキュメントを参照してください。
// このリクエストのキャッシュキーを"some-string"に設定します。fetch(event.request, { cf: { cacheKey: "some-string" } });通常、CloudflareはリクエストのURLに基づいてキャッシュキーを計算します。ただし、時には異なるURLをキャッシュ目的で同じように扱いたい場合があります。たとえば、ウェブサイトのコンテンツがAmazon S3とGoogle Cloud Storageの両方からホストされている場合、両方の場所に同じコンテンツがあり、Workerを使用してランダムにバランスを取ることができます。しかし、コンテンツの2つのコピーをキャッシュしたくはありません。カスタムキャッシュキーを利用して、サブリクエストURLではなく元のリクエストURLに基づいてキャッシュすることができます。
export default { async fetch(request) { let url = new URL(request.url);
if (Math.random() < 0.5) { url.hostname = "example.s3.amazonaws.com"; } else { url.hostname = "example.storage.googleapis.com"; }
let newRequest = new Request(url, request); return fetch(newRequest, { cf: { cacheKey: request.url }, }); },};export default { async fetch(request): Promise<Response> { let url = new URL(request.url);
if (Math.random() < 0.5) { url.hostname = "example.s3.amazonaws.com"; } else { url.hostname = "example.storage.googleapis.com"; }
let newRequest = new Request(url, request); return fetch(newRequest, { cf: { cacheKey: request.url }, }); },} satisfies ExportedHandler;異なるゾーンを代表して動作するWorkersは、お互いのキャッシュに影響を与えることはできません。キャッシュキーを上書きできるのは、自分のゾーン内でリクエストを行うときのみです(上記の例ではevent.request.urlが保存されたキーでした)、またはCloudflareにないホストへのリクエストです。別のCloudflareゾーン(たとえば、別のCloudflare顧客に属する)へのリクエストを行うと、そのゾーンはCloudflare内で自分のコンテンツがどのようにキャッシュされるかを完全に制御します。上書きすることはできません。
// 200ステータスコードの場合、86400秒間レスポンスをキャッシュさせる// 404の場合は1秒、500エラーはキャッシュしない。fetch(request, { cf: { cacheTtlByStatus: { "200-299": 86400, 404: 1, "500-599": 0 } },});このオプションは、レスポンスのステータスコードに基づいてTTLを選択し、cacheEverything: trueを自動的に設定しないcacheTtl機能のバージョンです。このリクエストに対するレスポンスが一致するステータスコードを持っている場合、Cloudflareは指示された時間だけキャッシュし、オリジンから送信されたキャッシュディレクティブを上書きします。cacheTtl機能の詳細については、リクエストページの詳細を確認できます。
カスタムキャッシュキーとレスポンスコードに基づく上書きを使用して、オリジンからのレスポンスステータスコードとリクエストファイルタイプに基づいてTTLを設定するWorkerを書くことができます。
以下の例は、ストリーミングメディアアセットのリクエストをキャッシュするためにこれを使用する方法を示しています:
export default { async fetch(request) { // 新しいURLをインスタンス化して変更可能にします const newRequest = new URL(request.url);
const customCacheKey = `${newRequest.hostname}${newRequest.pathname}`; const queryCacheKey = `${newRequest.hostname}${newRequest.pathname}${newRequest.search}`;
// 異なるアセットタイプは通常、異なるキャッシング戦略を持ちます。ほとんどの場合、ユーザー生成コンテンツでないオーディオ、ビデオ、画像などのメディアコンテンツは、頻繁に更新する必要がないため、長いTTLが最適です。しかし、HLSストリーミングでは、マニフェストファイルは通常短いTTLに設定されているため、再生に影響を与えないようにします。これらのファイルには、プレーヤーが必要とするデータが含まれています。アセットタイプのカテゴリごとにキャッシング戦略をオブジェクト内の配列に設定することで、アプリケーションのメディアコンテンツに関する複雑なニーズを解決できます。
const cacheAssets = [ { asset: "video", key: customCacheKey, regex: /(.*\/Video)|(.*\.(m4s|mp4|ts|avi|mpeg|mpg|mkv|bin|webm|vob|flv|m2ts|mts|3gp|m4v|wmv|qt))/, info: 0, ok: 31556952, redirects: 30, clientError: 10, serverError: 0, }, { asset: "image", key: queryCacheKey, regex: /(.*\/Images)|(.*\.(jpg|jpeg|png|bmp|pict|tif|tiff|webp|gif|heif|exif|bat|bpg|ppm|pgn|pbm|pnm))/, info: 0, ok: 3600, redirects: 30, clientError: 10, serverError: 0, }, { asset: "frontEnd", key: queryCacheKey, regex: /^.*\.(css|js)/, info: 0, ok: 3600, redirects: 30, clientError: 10, serverError: 0, }, { asset: "audio", key: customCacheKey, regex: /(.*\/Audio)|(.*\.(flac|aac|mp3|alac|aiff|wav|ogg|aiff|opus|ape|wma|3gp))/, info: 0, ok: 31556952, redirects: 30, clientError: 10, serverError: 0, }, { asset: "directPlay", key: customCacheKey, regex: /.*(\/Download)/, info: 0, ok: 31556952, redirects: 30, clientError: 10, serverError: 0, }, { asset: "manifest", key: customCacheKey, regex: /^.*\.(m3u8|mpd)/, info: 0, ok: 3, redirects: 2, clientError: 1, serverError: 0, }, ];
const { asset, regex, ...cache } = cacheAssets.find(({ regex }) => newRequest.pathname.match(regex)) ?? {};
const newResponse = await fetch(request, { cf: { cacheKey: cache.key, polish: false, cacheEverything: true, cacheTtlByStatus: { "100-199": cache.info, "200-299": cache.ok, "300-399": cache.redirects, "400-499": cache.clientError, "500-599": cache.serverError, }, cacheTags: ["static"], }, });
const response = new Response(newResponse.body, newResponse);
// デバッグ目的のため response.headers.set("debug", JSON.stringify(cache)); return response; },};addEventListener("fetch", (event) => { return event.respondWith(handleRequest(event.request));});
async function handleRequest(request) { // 新しいURLをインスタンス化して変更可能にします const newRequest = new URL(request.url);
// 後で配列で使用するために`const`を設定します const customCacheKey = `${newRequest.hostname}${newRequest.pathname}`; const queryCacheKey = `${newRequest.hostname}${newRequest.pathname}${newRequest.search}`;
// `cf`オブジェクト内でfetch APIを使用してCloudflareのキャッシュを操作するために必要なすべての変数を設定します。これらの変数を下のオブジェクトに渡します。 const cacheAssets = [ { asset: "video", key: customCacheKey, regex: /(.*\/Video)|(.*\.(m4s|mp4|ts|avi|mpeg|mpg|mkv|bin|webm|vob|flv|m2ts|mts|3gp|m4v|wmv|qt))/, info: 0, ok: 31556952, redirects: 30, clientError: 10, serverError: 0, }, { asset: "image", key: queryCacheKey, regex: /(.*\/Images)|(.*\.(jpg|jpeg|png|bmp|pict|tif|tiff|webp|gif|heif|exif|bat|bpg|ppm|pgn|pbm|pnm))/, info: 0, ok: 3600, redirects: 30, clientError: 10, serverError: 0, }, { asset: "frontEnd", key: queryCacheKey, regex: /^.*\.(css|js)/, info: 0, ok: 3600, redirects: 30, clientError: 10, serverError: 0, }, { asset: "audio", key: customCacheKey, regex: /(.*\/Audio)|(.*\.(flac|aac|mp3|alac|aiff|wav|ogg|aiff|opus|ape|wma|3gp))/, info: 0, ok: 31556952, redirects: 30, clientError: 10, serverError: 0, }, { asset: "directPlay", key: customCacheKey, regex: /.*(\/Download)/, info: 0, ok: 31556952, redirects: 30, clientError: 10, serverError: 0, }, { asset: "manifest", key: customCacheKey, regex: /^.*\.(m3u8|mpd)/, info: 0, ok: 3, redirects: 2, clientError: 1, serverError: 0, }, ];
// `.find` メソッドは配列 (`cacheAssets`) 内の要素を見つけるために使用されます。この場合、`regex` はファイル拡張子に基づいてキャッシュするために `.match` メソッドに渡されます。配列には多くのメディアタイプが含まれています。さらにタイプを追加したい場合は、配列を更新してください。詳細については、https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find を参照してください。 const { asset, regex, ...cache } = cacheAssets.find(({ regex }) => newRequest.pathname.match(regex)) ?? {};
const newResponse = await fetch(request, { cf: { cacheKey: cache.key, polish: false, cacheEverything: true, cacheTtlByStatus: { "100-199": cache.info, "200-299": cache.ok, "300-399": cache.redirects, "400-499": cache.clientError, "500-599": cache.serverError, }, cacheTags: ["static"], }, });
const response = new Response(newResponse.body, newResponse);
// デバッグ目的のため response.headers.set("debug", JSON.stringify(cache)); return response;}