このガイドは、Cloudflare のグローバルネットワークで Snippets と Workers のどちらを使うかを判断するときに使います。ベストプラクティス、比較、実際のユースケースを示し、ワークロードに合う製品を選べるようにします。
Cloudflare Snippets は、フルスタックのコンピュート基盤を用意せずに、エッジで HTTP リクエストとレスポンスをすばやく宣言的に変更する手段です。Snippets は Cloudflare Rules を拡張し、オリジンへ届く前のリクエストと、上流から戻ったあとのレスポンスを、JavaScript ベースのロジックで変更できます。
Snippets では次ができます。
- ヘッダーの変更、JWT の検証、複雑なリライトやリダイレクト。
- 失敗したリクエストを別オリジンへリトライし、独自のキャッシュ戦略を適用する。
- 複数の Snippet を順に実行し、各 Snippet がリクエストまたはレスポンスを変更して次へ渡す。
Snippets は すべての有料プラン に追加料金なしで含まれます。軽量なエッジロジックには、こちらが第一候補です。
一方、Cloudflare Workers は、状態、コンピュート、Cloudflare の Developer Platform との連携が必要なアプリケーション向けのフルスタックコンピュート基盤です。Workers は 従量課金 で、無料枠もあります。
Snippets は、エッジでの高速かつ追加料金なしのリクエスト/レスポンス変更に向いています。Cloudflare Rules を、追加インフラや外部ソリューションなしで拡張できます。
- Cloudflare のネットワーク上で直接適用する、超高速なトラフィック変更。
- 組み込みアクションを超えて Cloudflare Rules を拡張し、細かく制御する。
- VCL、EdgeWorkers、オンプレミスのロジックを置き換え、CDN 移行を簡単にする。
- ヘッダー変更、レスポンスのキャッシュ、リダイレクト。
- JavaScript でエッジロジックを開発ワークフローへ組み込む。
- 永続的な状態管理(セッションストレージやデータベースなど)。
- 計算負荷の高い処理(画像変換や AI inference など)。
- Durable Objects や D1 など、Developer Platform サービスとの深い連携。
- 次のような高度なランタイム機能が必要な場合:
- Ruleset Engine と Workers runtime による、超高速でエッジ向けに最適化された実行。
- すべての有料プラン に追加料金なしで含まれる。
- URI、user-agent、cookies など、数十のリクエスト属性による細かい一致。
- 順次実行 — 同じリクエストに複数の Snippets を 実行 し、変更を段階的に適用できる。
- Cloudflare Rules とのネイティブ連携 — Snippets は、より早い リクエストフェーズ で動く他製品のリクエスト変更を引き継ぐ。
- JavaScript と Web APIs のサポート。次を含む:
- 次のような、主要な Workers runtime 機能:
- Terraform による自動デプロイとバージョニング。
| 機能 | Snippets | Workers |
|---|---|---|
| リクエスト属性(ヘッダー、地理位置情報、cookies など)に基づいてスクリプトを実行する | ✅ | ❌ |
| 特定の URL ルートでコードを実行する | ✅ | ✅ |
| HTTP リクエスト/レスポンスを変更する、または 別のレスポンス を返す | ✅ | ✅ |
| ヘッダーを動的に 追加、削除、リライト する | ✅ | ✅ |
| エッジでアセットを キャッシュ する | ✅ | ✅ |
| オリジンサーバー 間でトラフィックを動的にルーティングする | ✅ | ✅ |
| リクエストの 認証、URL の 事前署名、A/B テスト の実行 | ✅ | ✅ |
| JavaScript と Web APIs でロジックを定義する | ✅ | ✅ |
| 計算負荷の高い処理(AI、画像変換 など) | ❌ | ✅ |
| 永続データを保存する(KV、Durable Objects、D1 など) | ❌ | ✅ |
| API や フルスタックアプリケーション を構築する | ❌ | ✅ |
| TypeScript、Python、Rust、その他のプログラミング 言語 を使う | ❌ | ✅ |
| HTTP 以外の プロトコル をサポートする | ❌ | ✅ |
| 実行 ログ を分析し、性能指標を追跡する | ❌ | ✅ |
| コマンドラインインターフェース(CLI) でデプロイする | ❌ | ✅ |
| 段階的にロールアウトし、以前の バージョン へロールバックする | ❌ | ✅ |
| Smart Placement で実行を最適化する | ❌ | ✅ |
以下は、Snippets の実際の使い方です。使い始めのテンプレートは 例 にもあります。
リクエストとレスポンスのヘッダーを動的に変更します。
export default {
async fetch(request) {
// Get the current timestamp
const timestamp = Date.now();
// Convert the timestamp to hexadecimal format
const hexTimestamp = timestamp.toString(16);
// Clone the request and add the custom header with HEX timestamp
const modifiedRequest = new Request(request, {
headers: new Headers(request.headers),
});
modifiedRequest.headers.set("X-Hex-Timestamp", hexTimestamp);
// Pass the modified request to the origin
const response = await fetch(modifiedRequest);
// Clone the response so that it's no longer immutable
const newResponse = new Response(response.body, response);
// Add a custom header with a value to the response
newResponse.headers.append(
"x-snippets-hello",
"Hello from Cloudflare Snippets",
);
// Delete headers from the response
newResponse.headers.delete("x-header-to-delete");
newResponse.headers.delete("x-header2-to-delete");
// Adjust the value for an existing header in the response
newResponse.headers.set("x-header-to-change", "NewValue");
// Serve modified response to the visitor
return newResponse;
},
};オリジンが計画メンテナンス中のとき、トラフィックをメンテナンスページへ向けます。
export default {
async fetch(request) {
return new Response(
`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>We'll Be Right Back!</title>
<style> body { font-family: Arial, sans-serif; text-align: center; padding: 20px; } </style>
</head>
<body>
<h1>We'll Be Right Back!</h1>
<p>Our site is undergoing maintenance. Check back soon!</p>
</body>
</html>
`,
{ status: 503, headers: { "Content-Type": "text/html" } },
);
},
};エッジでプログラムによるキャッシュを行い、オリジン負荷を下げます。
const CACHE_DURATION = 30 * 24 * 60 * 60; // 30 days
export default {
async fetch(request) {
const cache = caches.default;
const cacheKey = new Request(request.url, { method: "GET" });
let response = await cache.match(cacheKey);
if (!response) {
response = await fetch(request);
response = new Response(response.body, response);
response.headers.set("Cache-Control", `s-maxage=${CACHE_DURATION}`);
await cache.put(cacheKey, response.clone());
}
return response;
},
};訪問者の地理位置情報に基づいてリダイレクトします。
export default {
async fetch(request) {
const country = request.cf.country;
const redirectMap = {
US: "https://example.com/us",
EU: "https://example.com/eu",
};
if (redirectMap[country])
return Response.redirect(redirectMap[country], 301);
return fetch(request);
},
};オリジンが 403 Forbidden を返した場合、訪問者を別ページへリダイレクトします。
export default {
async fetch(request) {
// Send original request to the origin
const response = await fetch(request);
// Check if origin responded with 403 status code
if (response.status == 403) {
// If so, redirect to this URL
const destinationURL = "https://example.com";
// With this status code
const statusCode = 301;
// Serve redirect
return Response.redirect(destinationURL, statusCode);
}
// Otherwise, serve origin's response
else {
return response;
}
},
};元のリクエストへのレスポンスが 200 OK でもリダイレクトでもない場合、別オリジンへ送ります。
export default {
async fetch(request) {
// Send original request to the origin
const response = await fetch(request);
// If response is not 200 OK or a redirect, send to another origin
if (!response.ok && !response.redirected) {
// First, clone the original request to construct a new request
const newRequest = new Request(request);
// Add a header to identify a re-routed request at the new origin
newRequest.headers.set("X-Rerouted", "1");
// Clone the original URL
const url = new URL(request.url);
// Send request to a different origin / hostname
url.hostname = "example.com";
// Serve response to the new request from the origin
return await fetch(url, newRequest);
}
// If response is 200 OK or a redirect, serve it
return response;
},
};オリジンが JSON を返した場合、機密フィールドを削除してから訪問者へレスポンスを返します。
export default {
async fetch(request) {
// Send original request to the origin
const response = await fetch(request);
// Check if origin responded with JSON
try {
// Parse API response as JSON
var api_response = response.json();
// Specify the fields you want to delete. For example, to delete "botManagement" array from parsed JSON:
delete api_response.botManagement;
// Serve modified API response
return Response.json(api_response);
} catch (err) {
// On failure, serve unmodified origin's response
return response;
}
},
};Cross-Origin Resource Sharing(CORS) ↗ ヘッダーを調整し、プリフライトリクエストを処理します。
// Define CORS headers
const corsHeaders = {
"Access-Control-Allow-Origin": "*", // Replace * with your allowed origin(s)
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", // Adjust allowed methods as needed
"Access-Control-Allow-Headers": "Content-Type, Authorization", // Adjust allowed headers as needed
"Access-Control-Max-Age": "86400", // Adjust max age (in seconds) as needed
};
export default {
async fetch(request) {
// Make a copy of the request to modify its headers
const modifiedRequest = new Request(request);
// Handle preflight requests (OPTIONS)
if (request.method === "OPTIONS") {
return new Response(null, {
headers: {
...corsHeaders,
},
status: 200, // Respond with OK status for preflight requests
});
}
// Pass the modified request through to the origin
const response = await fetch(modifiedRequest);
// Make a copy of the response to modify its headers
const modifiedResponse = new Response(response.body, response);
// Set CORS headers on the response
Object.keys(corsHeaders).forEach((header) => {
modifiedResponse.headers.set(header, corsHeaders[header]);
});
return modifiedResponse;
},
};オリジンを変更せずに、古いリンクを置き換えます。
export default {
async fetch(request) {
// Define the old hostname here.
const OLD_URL = "oldsite.com";
// Then add your new hostname that should replace the old one.
const NEW_URL = "newsite.com";
class AttributeRewriter {
constructor(attributeName) {
this.attributeName = attributeName;
}
element(element) {
const attribute = element.getAttribute(this.attributeName);
if (attribute) {
element.setAttribute(
this.attributeName,
attribute.replace(OLD_URL, NEW_URL),
);
}
}
}
const rewriter = new HTMLRewriter()
.on("a", new AttributeRewriter("href"))
.on("img", new AttributeRewriter("src"));
const res = await fetch(request);
const contentType = res.headers.get("Content-Type");
// If the response is HTML, it can be transformed with
// HTMLRewriter -- otherwise, it should pass through
if (contentType.startsWith("text/html")) {
return rewriter.transform(res);
} else {
return res;
}
},
};ルールに一致した受信リクエストに遅延を入れます。不審なリクエストに使えます。
export default {
async fetch(request) {
// Define delay
const delay_in_seconds = 5;
// Introduce a delay
await new Promise((resolve) =>
setTimeout(resolve, delay_in_seconds * 1000),
); // Set delay in milliseconds
// Pass the request to the origin
const response = await fetch(request);
return response;
},
};Snippets と Workers は役割が違いますが、複雑なトラフィックワークフローでは組み合わせて使えます。
競合を避けるため、同じ URL で動かさず、別のリクエストパスで動かしてください。それぞれのロジック内で、相手の URL をサブリクエストとして fetch し、実行とキャッシュの動きを安定させます。
Snippets は Worker へ届く前に受信リクエストを変更できます。Workers はその変更を読み取り、追加の変換を行い、下流へ渡せます。
export default {
async fetch(request) {
// Get the current timestamp
const timestamp = Date.now();
const hexTimestamp = timestamp.toString(16);
// Clone request and add a custom header
const modifiedRequest = new Request(request, {
headers: new Headers(request.headers),
});
modifiedRequest.headers.set("X-Hex-Timestamp", hexTimestamp);
console.log(`X-Hex-Timestamp: ${hexTimestamp}`);
// Pass modified request to origin
return fetch(modifiedRequest);
},
};export default {
async fetch(request) {
const response = await fetch("https://{snippets_url}", request); // Ensure {snippets_url} points to the endpoint modified by Snippets
const newResponse = new Response(response.body, response);
let hexTimestamp = request.headers.get("X-Hex-Timestamp") || "null";
console.log(hexTimestamp);
newResponse.headers.set("X-Hex-Timestamp", hexTimestamp);
return newResponse;
},
};結果: Snippet が X-Hex-Timestamp を設定し、Worker がそれを読み取ってオリジンへ転送します。
Worker が計算負荷の高い処理(画像変換など)を行い、Snippet がキャッシュ済みの結果を返して不要な Worker 実行を避けます。Workers を キャッシュの前 で動かしたくない場合に役立ちます。
export default {
async fetch(request) {
const url = new URL(request.url);
url.hostname = "origin.example.com"; // Ensure this hostname points to the origin where the resource is hosted
const newRequest = new Request(url, request);
const customKey = `https://${url.hostname}${url.pathname}`; // This custom cache key should be the same in both Worker and Snippet configuration for cache to work
// Fetch and modify response
const response = await fetch(newRequest);
const newResponse = new Response(response.body, response);
// Cache the transformed response
const cache = caches.default;
const cachedResponse = newResponse.clone();
cachedResponse.headers.set("X-Cached-In-Workers", "true");
await cache.put(customKey, cachedResponse);
newResponse.headers.set("X-Retrieved-From-Workers", "true");
return newResponse;
},
};export default {
async fetch(request) {
const url = new URL(request.url);
url.hostname = "origin.example.com"; // Ensure this hostname points to the origin where the resource is hosted
const cacheKey = `https://${url.hostname}${url.pathname}`; // This custom cache key should be the same in both Worker and Snippet configuration for cache to work
// Access cache
const cache = caches.default;
let response = await cache.match(cacheKey);
if (!response) {
console.log(`Cache miss for: ${cacheKey}. Fetching from Worker...`);
url.hostname = "worker.example.com"; // Ensure this hostname points to the Workers route
response = await fetch(new Request(url, request));
// Cache the response for future use
response = new Response(response.body, response);
response.headers.set("Cache-Control", `s-maxage=3600`);
response.headers.set("x-snippets-cache", "stored");
} else {
console.log(`Cache hit for: ${cacheKey}`);
response = new Response(response.body, response);
response.headers.set("x-snippets-cache", "hit");
}
return response;
},
};結果: 変換済みレスポンス(X-Cached-In-Workers: true)がキャッシュから返され、余分な Worker 実行を避けます(X-Retrieved-From-Workers は付きません)。キャッシュが切れると、Snippet は新しい版を取得します。
Snippets と Workers は同じ Workers runtime を共有します。bindings、永続ストレージ、高度な実行機能に依存しない JavaScript コードは、両者の間でそのまま移行できます。
次に当てはまる Worker は、Snippets への移行を検討してください。
- ヘッダー、リダイレクト、キャッシュルール、オリジンルーティングだけを変更している。
- bindings、永続ストレージ、外部連携が不要。
- 単純なロジックの軽量な JavaScript 関数である。
- Pro、Business、または Enterprise プランで、回数無制限に無料で動かしたい。
Snippets へ移すと、次ができます。
- Ruleset Engine による高度なリクエスト一致を使う。
- 従量課金をなくす — Snippets はすべての有料プランに 追加料金なしで含まれます。
- トラフィック変更を Cloudflare Rules へ直接組み込み、管理を簡単にする。
ロジックが次に当てはまる場合は、Snippets から Workers へ移行してください。
- 実行時間、メモリ、その他の 上限 を超える。
- 次のような永続的な状態管理が必要:
- 次のような計算負荷の高い処理を行う:
- Cloudflare の Developer Platform と連携する。
- ユニットテスト が必要。
- CLI(Wrangler)によるデプロイ自動化が必要。
Snippet が実行時間、メモリ、機能の上限に達した場合は、Workers へ移すと、制限なくロジックをスケールできます。
Cloudflare Snippets は、高速で宣言的なエッジトラフィックロジック向けの本番利用可能な手段です。Cloudflare Rules と Developer Platform の間をつなぎます。
Snippets と Workers が解く問題は異なります。
- ヘッダーのリライト、キャッシュ、リダイレクト、オリジンルーティング、カスタムレスポンス、A/B テスト、認証など、エッジでの高速で軽量なトラフィック変更には Snippets を使います。
- Workers は、高度なコンピュート、永続状態、フルスタックアプリケーション向けです。