From 70a592bc09d2efad883b0c43454d63cf5262eda2 Mon Sep 17 00:00:00 2001 From: Nikita Cano Date: Fri, 14 Mar 2025 18:03:46 +0000 Subject: [PATCH 1/4] Reference guide when to use Snippets vs Workers --- src/content/docs/rules/snippets/index.mdx | 5 +- .../docs/rules/snippets/when-to-use.mdx | 555 ++++++++++++++++++ 2 files changed, 559 insertions(+), 1 deletion(-) create mode 100644 src/content/docs/rules/snippets/when-to-use.mdx diff --git a/src/content/docs/rules/snippets/index.mdx b/src/content/docs/rules/snippets/index.mdx index a740b47f1cef88..9de1fc0204b403 100644 --- a/src/content/docs/rules/snippets/index.mdx +++ b/src/content/docs/rules/snippets/index.mdx @@ -49,7 +49,7 @@ Each subr ## Limits -Cloudflare Snippets are lightweight compared to [Cloudflare Workers](/workers/). The following limits apply: +Cloudflare Snippets are designed for fast, lightweight edge logic. The following limits apply: | Description | All plans | | -------------------------- | --------- | @@ -57,6 +57,9 @@ Cloudflare Snippets are lightweight compared to [Cloudflare Workers](/workers/). | Maximum memory | 2 MB | | Maximum total package size | 32 KB | +πŸ” **Need guidance on choosing between Snippets and Workers?** +Explore our [detailed guide](/rules/snippets/when-to-use/) for best practices, real-world use cases, and example implementations. + ## Execution order diff --git a/src/content/docs/rules/snippets/when-to-use.mdx b/src/content/docs/rules/snippets/when-to-use.mdx new file mode 100644 index 00000000000000..4783a616d1cba9 --- /dev/null +++ b/src/content/docs/rules/snippets/when-to-use.mdx @@ -0,0 +1,555 @@ +--- +title: When to use Snippets vs Workers +pcx_content_type: design-guide +sidebar: + order: 8 +head: + - tag: title + content: When to use Snippets vs Workers +--- + +## Introduction + +This guide helps you determine when to use Snippets or Workers on Cloudflare’s edge network. It provides best practices, comparisons, and real-world use cases to help you choose the right product for your workload. + +### What are Snippets? +Cloudflare Snippets provide a fast, declarative way to modify HTTP requests and responses at the edge β€” without requiring a full-stack compute platform. Snippets extend [Cloudflare Rules](/rules/) by allowing you to write JavaScript-based logic that modifies requests before they reach an origin and responses after they return from upstream. + +Snippets enable you to: +- Modify headers, validate JWTs, and implement complex rewrites or redirects. +- Retry failed requests to different origins and apply custom caching strategies. +- Execute multiple Snippets sequentially, with each Snippet modifying the request or response before handing it off to the next. + +Snippets are included at no additional cost in [all paid plans](/rules/snippets/#availability), making them the preferred solution for lightweight edge logic. + +### What are Workers? +By contrast, [Cloudflare Workers](/workers/) provide a full-stack compute platform designed for applications requiring state, compute, and integrations with Cloudflare’s [Developer Platform](/learning-paths/workers/devplat/intro-to-devplat/). Workers operate on a [usage-based pricing model](/workers/platform/pricing/) and include a free tier. + +--- + +## Choosing the right product + +Snippets are ideal for fast, cost-free request and response modifications at the edge. They extend [Cloudflare Rules](/rules/) without requiring additional infrastructure or external solutions. + +### When to use Snippets +- Ultra-fast traffic modifications applied directly on Cloudflare’s network. +- Extend Cloudflare Rules beyond built-in actions for greater control. +- Simplify CDN migrations by replacing VCL, EdgeWorkers, or on-premise logic. +- Modify headers, cache responses, and perform redirects. +- Integrate edge logic into development workflows using JavaScript. + +### What Snippets are not designed for +- Persistent state management (for example, session storage, databases). +- Compute-intensive tasks (for example, image transformations, [AI inference](/workers-ai/)). +- Deep integrations with [Developer Platform](/learning-paths/workers/devplat/intro-to-devplat/) services like [Durable Objects](/durable-objects/) or [D1](/d1/). +- Use cases requiring advanced runtime features, such as: + - [Environment variables](/workers/configuration/environment-variables/) + - [Bindings](/workers/runtime-apis/bindings/) + - [Cron triggers](/workers/configuration/cron-triggers/) + - High [compute limits](/rules/snippets/#limits) + +### Key features + +- Ultra-fast, edge-optimized execution, powered by [Ruleset Engine](/ruleset-engine/) and [Workers runtime](/workers/runtime-apis/). +- Included at no additional cost on [all paid plans](/rules/snippets/#availability). +- Granular request matching using dozens of request attributes, including [URI](/ruleset-engine/rules-language/fields/reference/http.request.full_uri/), [user-agent](/ruleset-engine/rules-language/fields/reference/http.user_agent/), [cookies](/ruleset-engine/rules-language/fields/reference/http.request.cookies/), and more. +- Sequential execution – multiple Snippets [can run](/rules/snippets/how-it-works/) on the same request, applying modifications step by step. +- Native integration with [Cloudflare Rules](/rules/) β€” Snippets inherit request modifications from other products running in earlier [request phases](/ruleset-engine/reference/phases-list/#request-phases). +- JavaScript and Web APIs support, including: + - [fetch API](/workers/runtime-apis/fetch/) + - [cache API](/workers/runtime-apis/cache/) +- Essential [Workers runtime](/workers/runtime-apis/) features, such as: + - [request.cf object](/workers/runtime-apis/request/#incomingrequestcfproperties) + - [HTMLRewriter](/workers/runtime-apis/html-rewriter/) +- Automated deployment and versioning via [Terraform](/rules/snippets/create-terraform/). + +--- + +## Snippets vs Workers: Feature comparison + +| Feature | Snippets | Workers | +|---------------------------------|------------|------------| +| Execute scripts based on request attributes (headers, geolocation, cookies, etc.) | βœ… | ❌ | +| Execute code on a specific URL route | βœ… | βœ… | +| Modify HTTP requests/responses or serve a [different response](/rules/snippets/examples/maintenance/) | βœ… | βœ… | +| [Add](/rules/snippets/examples/hex-timestamp/), [remove](/rules/snippets/examples/remove-response-headers/), or [rewrite](/rules/snippets/examples/override-set-cookies-value/) headers dynamically | βœ… | βœ… | +| [Cache](/rules/snippets/examples/custom-cache/) assets at the edge | βœ… | βœ… | +| Route traffic dynamically between [origins](/rules/snippets/examples/serve-different-origin/) | βœ… | βœ… | +| [Authenticate](/rules/snippets/examples/auth-with-headers/) requests, [pre-sign](/cache/interaction-cloudflare-products/waf-snippets/) URLs, run [A/B testing](/rules/snippets/examples/ab-testing-same-url/) | βœ… | βœ… | +| Define logic using [JavaScript and Web APIs](/workers/languages/javascript/) | βœ… | βœ… | +| Perform compute-heavy tasks (for example, [AI](/workers-ai/), [image transformations](/images/transform-images/transform-via-workers/)) | ❌ | βœ… | +| Store persistent data (for example, [KV](/kv/), [Durable Objects](/durable-objects/), [D1](/d1/)) | ❌ | βœ… | +| Build [APIs](/d1/tutorials/build-a-comments-api/) and [full-stack applications](/pages/framework-guides/deploy-an-astro-site/#video-tutorial) | ❌ | βœ… | +| Use TypeScript, Python, Rust or other programming [languages](/workers/languages/) | ❌ | βœ… | +| Support non-HTTP [protocols](/workers/reference/protocols/) | ❌ | βœ… | +| Analyze execution [logs](/workers/observability/logs/workers-logs/) and track performance metrics | ❌ | βœ… | +| Deploy via [command-line interface (CLI)](/workers/wrangler/) | ❌ | βœ… | +| Roll out gradually, roll back to previous [versions](/workers/configuration/versions-and-deployments/) | ❌ | βœ… | +| Optimize execution with [Smart Placement](/workers/configuration/smart-placement/) | ❌ | βœ… | + +--- + +## Code examples: Common Snippets templates + +Below are practical use cases demonstrating Snippets in action. You can find more templates to get started in the [Examples](/rules/snippets/examples/) section. + +### Modify HTTP headers + +**Use case:** Modify request and response headers dynamically. + +```javascript +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; + }, +}; +``` + +### Serve a custom maintenance page + +**Use case:** Route traffic to a maintenance page when your origin is undergoing a planned maintenance. + +```javascript +export default { + async fetch(request) { + return new Response(` + + + + + We'll Be Right Back! + + + +

We'll Be Right Back!

+

Our site is undergoing maintenance. Check back soon!

+ + + `, { status: 503, headers: { "Content-Type": "text/html" } }); + } +}; +``` + +### Custom cache + +**Use case:** Programmatic caching at the edge to reduce origin load. + +```javascript +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; + } +}; +``` + +### Redirect based on country code + +**Use case:** Redirect users based on their geographic location. + +```javascript +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); + } +}; +``` + +### Redirect 403 Forbidden to a different page + +**Use case:** If origin responded with `403 Forbidden` error code, redirect visitor to a different page. + +```javascript +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; + } + }, +}; +``` + +### Retry to another origin + +**Use case:** If response to the original request is not `200 OK` or a redirect, send to another origin. + +```javascript +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; + }, +}; +``` + +### Remove fields from API response + +**Use case:** If origin responds with JSON, delete sensitive fields before returning a response to visitor. + +```javascript +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; + } + }, +}; +``` + +### Set CORS headers + +**Use case:** Adjust [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) headers and handle preflight requests. + +```javascript +// 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; + }, +}; +``` + +### Rewrite links on HTML pages + +**Use case:** Replace outdated links without making changes on your origin. + +```javascript +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; + } + }, +}; +``` + +### Slow down requests + +**Use case:** Define a delay to be used when incoming requests match your rule. Useful for suspicious requests. + +```javascript +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; + }, +}; +``` + +--- + +## Using Snippets and Workers together + +While Snippets and Workers have distinct capabilities, they can work together to handle complex traffic workflows. + +To avoid conflicts, Snippets and Workers should operate on separate request paths β€” rather than running on the same URL. Have them fetch their respective URLs as a subrequest within their logic, ensuring smooth execution and caching behavior. + +### Example 1: Passing data between Snippets and Workers + +Snippets can modify incoming requests before they reach a Worker, and Workers can read these modifications, perform additional transformations, and pass them downstream. + +#### Snippet: Adding a custom header +```javascript +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); + }, +}; +``` + +#### Worker: Reading a header and adding it to the response +```javascript +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; + } +}; +``` + +**Result:** The Snippet sets `X-Hex-Timestamp`, which the Worker reads and forwards to the origin. + + +### Example 2: Caching Worker responses using Snippets + +A Worker performs compute-heavy processing (for example, image transformation), while a Snippet serves cached results to avoid unnecessary Worker execution. This can be helpful in situations when running Workers [before cache](/cache/interaction-cloudflare-products/workers/) is not desirable. + +#### Worker: transforming and caching responses +```javascript +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; + } +}; +``` + +#### Snippet: Serving cached responses or forwarding to Worker +```javascript +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; + } +}; +``` + +**Result:** The transformed response (`X-Cached-In-Workers: true`) is served from cache, avoiding redundant Worker execution (`X-Retrieved-From-Workers` is not present). When cache expires, the Snippet fetches a fresh version. + +--- + +## Migrating between Snippets and Workers + +Snippets and Workers share the same [Workers runtime](/workers/runtime-apis/), meaning JavaScript code that does not rely on bindings, persistent storage, or advanced execution features can be migrated seamlessly between them. + +### When to migrate workloads to Snippets +You should consider migrating a Worker to Snippets if it: +- Only modifies headers, redirects, caching rules, or origin routing. +- Does not require bindings, persistent storage, or external integrations. +- Is a lightweight JavaScript function with simple logic. +- Needs to run an unlimited number of times for free on a Pro, Business, or Enterprise plan. + +Migrating to Snippets allows you to: +- Leverage advanced request matching via the [Ruleset Engine](/ruleset-engine/). +- Eliminate usage-based billing β€” Snippets are [included at no cost](/rules/snippets/#availability) on all paid plans. +- Simplify management by integrating traffic modifications directly into Cloudflare Rules. + +### When to migrate workloads to Workers +You should migrate from Snippets to Workers if your logic: +- Exceeds execution time, memory, or other [limits](/rules/snippets/#limits). +- Requires persistent state management, such as: + - [Key-Value (KV) storage](/kv/) + - [SQL databases (D1)](/d1/) + - [Durable Objects](/durable-objects/) +- Performs compute-intensive operations, including: + - [AI inference](/workers-ai/) + - [Vector search](/vectorize/) + - [Image transformations](/images/transform-images/transform-via-workers/) +- Interacts with Cloudflare’s [Developer Platform](/learning-paths/workers/devplat/intro-to-devplat/). +- Requires [unit testing](/workers/testing/). +- Needs deployment automation via the CLI ([Wrangler](/workers/wrangler/)). + +If your Snippet reaches the limits of execution time, memory, or functionality, transitioning to Workers ensures your logic can scale without restrictions. + +--- + +## Conclusion + +Cloudflare Snippets provide a production-ready solution for fast, declarative edge traffic logic, bridging the gap between [Cloudflare Rules](/rules/) and [Developer Platform](/learning-paths/workers/devplat/intro-to-devplat/). + +Snippets and Workers solve different problems: +- Use Snippets for fast, lightweight traffic modifications at the edge, including header rewrites, caching, redirects, origin routing, custom responses, A/B testing and authentication. +- Workers are built for advanced compute, persistent state, and full-stack applications. \ No newline at end of file From 8375a4d067523c9f3a48ff7a3b29a7bd28ee4d12 Mon Sep 17 00:00:00 2001 From: Pedro Sousa <680496+pedrosousa@users.noreply.github.com> Date: Mon, 17 Mar 2025 17:55:24 +0000 Subject: [PATCH 2/4] Update formatting --- src/content/docs/rules/snippets/index.mdx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/content/docs/rules/snippets/index.mdx b/src/content/docs/rules/snippets/index.mdx index 9de1fc0204b403..b8bc56c8b51aec 100644 --- a/src/content/docs/rules/snippets/index.mdx +++ b/src/content/docs/rules/snippets/index.mdx @@ -11,7 +11,7 @@ head: content: Cloudflare Snippets (beta) --- -import { FeatureTable, GlossaryTooltip, Render } from "~/components"; +import { FeatureTable, GlossaryTooltip, Render, Card } from "~/components"; Cloudflare Snippets (beta) provide a powerful and flexible way to customize the behavior of your website or application using short pieces of JavaScript code. With Snippets, you can modify HTTP response headers, implement JWT validation, perform complex redirects, and much more. @@ -57,9 +57,12 @@ Cloudflare Snippets are designed for fast, lightweight edge logic. The following | Maximum memory | 2 MB | | Maximum total package size | 32 KB | -πŸ” **Need guidance on choosing between Snippets and Workers?** + + Explore our [detailed guide](/rules/snippets/when-to-use/) for best practices, real-world use cases, and example implementations. + + ## Execution order From 24a5d2d1b9683162e2d01eb10a33c772aed6d5a4 Mon Sep 17 00:00:00 2001 From: Pedro Sousa <680496+pedrosousa@users.noreply.github.com> Date: Mon, 17 Mar 2025 17:57:35 +0000 Subject: [PATCH 3/4] PCX review --- .../docs/rules/snippets/when-to-use.mdx | 650 +++++++++--------- 1 file changed, 333 insertions(+), 317 deletions(-) diff --git a/src/content/docs/rules/snippets/when-to-use.mdx b/src/content/docs/rules/snippets/when-to-use.mdx index 4783a616d1cba9..6f7db604d12091 100644 --- a/src/content/docs/rules/snippets/when-to-use.mdx +++ b/src/content/docs/rules/snippets/when-to-use.mdx @@ -3,26 +3,24 @@ title: When to use Snippets vs Workers pcx_content_type: design-guide sidebar: order: 8 -head: - - tag: title - content: When to use Snippets vs Workers --- -## Introduction +This guide helps you determine when to use Snippets or Workers on Cloudflare's global network. It provides best practices, comparisons, and real-world use cases to help you choose the right product for your workload. -This guide helps you determine when to use Snippets or Workers on Cloudflare’s edge network. It provides best practices, comparisons, and real-world use cases to help you choose the right product for your workload. +## What are Snippets? -### What are Snippets? Cloudflare Snippets provide a fast, declarative way to modify HTTP requests and responses at the edge β€” without requiring a full-stack compute platform. Snippets extend [Cloudflare Rules](/rules/) by allowing you to write JavaScript-based logic that modifies requests before they reach an origin and responses after they return from upstream. Snippets enable you to: + - Modify headers, validate JWTs, and implement complex rewrites or redirects. - Retry failed requests to different origins and apply custom caching strategies. - Execute multiple Snippets sequentially, with each Snippet modifying the request or response before handing it off to the next. Snippets are included at no additional cost in [all paid plans](/rules/snippets/#availability), making them the preferred solution for lightweight edge logic. -### What are Workers? +## What are Workers? + By contrast, [Cloudflare Workers](/workers/) provide a full-stack compute platform designed for applications requiring state, compute, and integrations with Cloudflare’s [Developer Platform](/learning-paths/workers/devplat/intro-to-devplat/). Workers operate on a [usage-based pricing model](/workers/platform/pricing/) and include a free tier. --- @@ -32,15 +30,17 @@ By contrast, [Cloudflare Workers](/workers/) provide a full-stack compute platfo Snippets are ideal for fast, cost-free request and response modifications at the edge. They extend [Cloudflare Rules](/rules/) without requiring additional infrastructure or external solutions. ### When to use Snippets -- Ultra-fast traffic modifications applied directly on Cloudflare’s network. + +- Ultra-fast traffic modifications applied directly on Cloudflare's network. - Extend Cloudflare Rules beyond built-in actions for greater control. - Simplify CDN migrations by replacing VCL, EdgeWorkers, or on-premise logic. - Modify headers, cache responses, and perform redirects. - Integrate edge logic into development workflows using JavaScript. ### What Snippets are not designed for -- Persistent state management (for example, session storage, databases). -- Compute-intensive tasks (for example, image transformations, [AI inference](/workers-ai/)). + +- Persistent state management (for example, session storage or databases). +- Compute-intensive tasks (for example, image transformations or [AI inference](/workers-ai/)). - Deep integrations with [Developer Platform](/learning-paths/workers/devplat/intro-to-devplat/) services like [Durable Objects](/durable-objects/) or [D1](/d1/). - Use cases requiring advanced runtime features, such as: - [Environment variables](/workers/configuration/environment-variables/) @@ -52,40 +52,40 @@ Snippets are ideal for fast, cost-free request and response modifications at the - Ultra-fast, edge-optimized execution, powered by [Ruleset Engine](/ruleset-engine/) and [Workers runtime](/workers/runtime-apis/). - Included at no additional cost on [all paid plans](/rules/snippets/#availability). -- Granular request matching using dozens of request attributes, including [URI](/ruleset-engine/rules-language/fields/reference/http.request.full_uri/), [user-agent](/ruleset-engine/rules-language/fields/reference/http.user_agent/), [cookies](/ruleset-engine/rules-language/fields/reference/http.request.cookies/), and more. +- Granular request matching using dozens of request attributes, such as [URI](/ruleset-engine/rules-language/fields/reference/http.request.full_uri/), [user-agent](/ruleset-engine/rules-language/fields/reference/http.user_agent/), and[cookies](/ruleset-engine/rules-language/fields/reference/http.request.cookies/). - Sequential execution – multiple Snippets [can run](/rules/snippets/how-it-works/) on the same request, applying modifications step by step. -- Native integration with [Cloudflare Rules](/rules/) β€” Snippets inherit request modifications from other products running in earlier [request phases](/ruleset-engine/reference/phases-list/#request-phases). +- Native integration with [Cloudflare Rules](/rules/) – Snippets inherit request modifications from other products running in earlier [request phases](/ruleset-engine/reference/phases-list/#request-phases). - JavaScript and Web APIs support, including: - - [fetch API](/workers/runtime-apis/fetch/) - - [cache API](/workers/runtime-apis/cache/) + - [Fetch API](/workers/runtime-apis/fetch/) + - [Cache API](/workers/runtime-apis/cache/) - Essential [Workers runtime](/workers/runtime-apis/) features, such as: - - [request.cf object](/workers/runtime-apis/request/#incomingrequestcfproperties) - - [HTMLRewriter](/workers/runtime-apis/html-rewriter/) + - [`request.cf` object](/workers/runtime-apis/request/#incomingrequestcfproperties) + - [`HTMLRewriter`](/workers/runtime-apis/html-rewriter/) - Automated deployment and versioning via [Terraform](/rules/snippets/create-terraform/). --- ## Snippets vs Workers: Feature comparison -| Feature | Snippets | Workers | -|---------------------------------|------------|------------| -| Execute scripts based on request attributes (headers, geolocation, cookies, etc.) | βœ… | ❌ | -| Execute code on a specific URL route | βœ… | βœ… | -| Modify HTTP requests/responses or serve a [different response](/rules/snippets/examples/maintenance/) | βœ… | βœ… | -| [Add](/rules/snippets/examples/hex-timestamp/), [remove](/rules/snippets/examples/remove-response-headers/), or [rewrite](/rules/snippets/examples/override-set-cookies-value/) headers dynamically | βœ… | βœ… | -| [Cache](/rules/snippets/examples/custom-cache/) assets at the edge | βœ… | βœ… | -| Route traffic dynamically between [origins](/rules/snippets/examples/serve-different-origin/) | βœ… | βœ… | -| [Authenticate](/rules/snippets/examples/auth-with-headers/) requests, [pre-sign](/cache/interaction-cloudflare-products/waf-snippets/) URLs, run [A/B testing](/rules/snippets/examples/ab-testing-same-url/) | βœ… | βœ… | -| Define logic using [JavaScript and Web APIs](/workers/languages/javascript/) | βœ… | βœ… | -| Perform compute-heavy tasks (for example, [AI](/workers-ai/), [image transformations](/images/transform-images/transform-via-workers/)) | ❌ | βœ… | -| Store persistent data (for example, [KV](/kv/), [Durable Objects](/durable-objects/), [D1](/d1/)) | ❌ | βœ… | -| Build [APIs](/d1/tutorials/build-a-comments-api/) and [full-stack applications](/pages/framework-guides/deploy-an-astro-site/#video-tutorial) | ❌ | βœ… | -| Use TypeScript, Python, Rust or other programming [languages](/workers/languages/) | ❌ | βœ… | -| Support non-HTTP [protocols](/workers/reference/protocols/) | ❌ | βœ… | -| Analyze execution [logs](/workers/observability/logs/workers-logs/) and track performance metrics | ❌ | βœ… | -| Deploy via [command-line interface (CLI)](/workers/wrangler/) | ❌ | βœ… | -| Roll out gradually, roll back to previous [versions](/workers/configuration/versions-and-deployments/) | ❌ | βœ… | -| Optimize execution with [Smart Placement](/workers/configuration/smart-placement/) | ❌ | βœ… | +| Feature | Snippets | Workers | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-----: | +| Execute scripts based on request attributes (for example, headers, geolocation, and cookies) | βœ… | ❌ | +| Execute code on a specific URL route | βœ… | βœ… | +| Modify HTTP requests/responses or serve a [different response](/rules/snippets/examples/maintenance/) | βœ… | βœ… | +| [Add](/rules/snippets/examples/hex-timestamp/), [remove](/rules/snippets/examples/remove-response-headers/), or [rewrite](/rules/snippets/examples/override-set-cookies-value/) headers dynamically | βœ… | βœ… | +| [Cache](/rules/snippets/examples/custom-cache/) assets at the edge | βœ… | βœ… | +| Route traffic dynamically between [origin servers](/rules/snippets/examples/serve-different-origin/) | βœ… | βœ… | +| [Authenticate](/rules/snippets/examples/auth-with-headers/) requests, [pre-sign](/cache/interaction-cloudflare-products/waf-snippets/) URLs, run [A/B testing](/rules/snippets/examples/ab-testing-same-url/) | βœ… | βœ… | +| Define logic using [JavaScript and Web APIs](/workers/languages/javascript/) | βœ… | βœ… | +| Perform compute-heavy tasks (for example, [AI](/workers-ai/), [image transformations](/images/transform-images/transform-via-workers/)) | ❌ | βœ… | +| Store persistent data (for example, [KV](/kv/), [Durable Objects](/durable-objects/), [D1](/d1/)) | ❌ | βœ… | +| Build [APIs](/d1/tutorials/build-a-comments-api/) and [full-stack applications](/pages/framework-guides/deploy-an-astro-site/#video-tutorial) | ❌ | βœ… | +| Use TypeScript, Python, Rust, or other programming [languages](/workers/languages/) | ❌ | βœ… | +| Support non-HTTP [protocols](/workers/reference/protocols/) | ❌ | βœ… | +| Analyze execution [logs](/workers/observability/logs/workers-logs/) and track performance metrics | ❌ | βœ… | +| Deploy via [command-line interface (CLI)](/workers/wrangler/) | ❌ | βœ… | +| Roll out gradually, roll back to previous [versions](/workers/configuration/versions-and-deployments/) | ❌ | βœ… | +| Optimize execution with [Smart Placement](/workers/configuration/smart-placement/) | ❌ | βœ… | --- @@ -95,56 +95,57 @@ Below are practical use cases demonstrating Snippets in action. You can find mor ### Modify HTTP headers -**Use case:** Modify request and response headers dynamically. +Modifies request and response headers dynamically. ```javascript 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; - }, + 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; + }, }; ``` ### Serve a custom maintenance page -**Use case:** Route traffic to a maintenance page when your origin is undergoing a planned maintenance. +Routes traffic to a maintenance page when your origin is undergoing a planned maintenance. ```javascript export default { - async fetch(request) { - return new Response(` + async fetch(request) { + return new Response( + ` @@ -157,238 +158,244 @@ export default {

Our site is undergoing maintenance. Check back soon!

- `, { status: 503, headers: { "Content-Type": "text/html" } }); - } + `, + { status: 503, headers: { "Content-Type": "text/html" } }, + ); + }, }; ``` ### Custom cache -**Use case:** Programmatic caching at the edge to reduce origin load. +Performs programmatic caching at the edge to reduce origin load. ```javascript 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; - } + 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; + }, }; ``` ### Redirect based on country code -**Use case:** Redirect users based on their geographic location. +Redirects visitors based on their geographic location. ```javascript 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); - } + 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); + }, }; ``` ### Redirect 403 Forbidden to a different page -**Use case:** If origin responded with `403 Forbidden` error code, redirect visitor to a different page. +If the origin responded with `403 Forbidden` error code, redirects visitor to a different page. ```javascript 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; - } - }, + 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; + } + }, }; ``` ### Retry to another origin -**Use case:** If response to the original request is not `200 OK` or a redirect, send to another origin. +If the response to the original request is not `200 OK` or a redirect, sends to another origin. ```javascript 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; - }, + 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; + }, }; ``` ### Remove fields from API response -**Use case:** If origin responds with JSON, delete sensitive fields before returning a response to visitor. +If the origin responds with JSON, deletes sensitive fields before returning a response to the visitor. ```javascript 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; - } - }, + 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; + } + }, }; ``` ### Set CORS headers -**Use case:** Adjust [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) headers and handle preflight requests. +Adjusts [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) headers and handles preflight requests. ```javascript // 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 + "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; - }, + 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; + }, }; ``` ### Rewrite links on HTML pages -**Use case:** Replace outdated links without making changes on your origin. +Replaces outdated links without having to make changes on your origin. ```javascript 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; - } - }, + 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; + } + }, }; ``` ### Slow down requests -**Use case:** Define a delay to be used when incoming requests match your rule. Useful for suspicious requests. +Defines a delay to be used when incoming requests match your rule. Useful for suspicious requests. ```javascript 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; - }, + 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; + }, }; ``` @@ -396,114 +403,117 @@ export default { ## Using Snippets and Workers together -While Snippets and Workers have distinct capabilities, they can work together to handle complex traffic workflows. +While Snippets and Workers have distinct capabilities, they can work together to handle complex traffic workflows. -To avoid conflicts, Snippets and Workers should operate on separate request paths β€” rather than running on the same URL. Have them fetch their respective URLs as a subrequest within their logic, ensuring smooth execution and caching behavior. +To avoid conflicts, Snippets and Workers should operate on separate request paths rather than running on the same URL. Have them fetch their respective URLs as a subrequest within their logic, ensuring smooth execution and caching behavior. ### Example 1: Passing data between Snippets and Workers Snippets can modify incoming requests before they reach a Worker, and Workers can read these modifications, perform additional transformations, and pass them downstream. -#### Snippet: Adding a custom header +#### Snippet: Add a custom header + ```javascript 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); - }, + 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); + }, }; ``` -#### Worker: Reading a header and adding it to the response +#### Worker: Read a header and add it to the response + ```javascript 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); + 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); + let hexTimestamp = request.headers.get("X-Hex-Timestamp") || "null"; + console.log(hexTimestamp); - newResponse.headers.set("X-Hex-Timestamp", hexTimestamp); - return newResponse; - } + newResponse.headers.set("X-Hex-Timestamp", hexTimestamp); + return newResponse; + }, }; ``` **Result:** The Snippet sets `X-Hex-Timestamp`, which the Worker reads and forwards to the origin. - ### Example 2: Caching Worker responses using Snippets A Worker performs compute-heavy processing (for example, image transformation), while a Snippet serves cached results to avoid unnecessary Worker execution. This can be helpful in situations when running Workers [before cache](/cache/interaction-cloudflare-products/workers/) is not desirable. -#### Worker: transforming and caching responses +#### Worker: Transform and cache responses + ```javascript 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; - } + 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; + }, }; ``` -#### Snippet: Serving cached responses or forwarding to Worker +#### Snippet: Serve cached responses or forward to Worker + ```javascript 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; - } + 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; + }, }; ``` @@ -511,24 +521,29 @@ export default { --- -## Migrating between Snippets and Workers +## Migration between Snippets and Workers Snippets and Workers share the same [Workers runtime](/workers/runtime-apis/), meaning JavaScript code that does not rely on bindings, persistent storage, or advanced execution features can be migrated seamlessly between them. ### When to migrate workloads to Snippets + You should consider migrating a Worker to Snippets if it: + - Only modifies headers, redirects, caching rules, or origin routing. - Does not require bindings, persistent storage, or external integrations. - Is a lightweight JavaScript function with simple logic. - Needs to run an unlimited number of times for free on a Pro, Business, or Enterprise plan. Migrating to Snippets allows you to: + - Leverage advanced request matching via the [Ruleset Engine](/ruleset-engine/). - Eliminate usage-based billing β€” Snippets are [included at no cost](/rules/snippets/#availability) on all paid plans. - Simplify management by integrating traffic modifications directly into Cloudflare Rules. ### When to migrate workloads to Workers + You should migrate from Snippets to Workers if your logic: + - Exceeds execution time, memory, or other [limits](/rules/snippets/#limits). - Requires persistent state management, such as: - [Key-Value (KV) storage](/kv/) @@ -538,9 +553,9 @@ You should migrate from Snippets to Workers if your logic: - [AI inference](/workers-ai/) - [Vector search](/vectorize/) - [Image transformations](/images/transform-images/transform-via-workers/) -- Interacts with Cloudflare’s [Developer Platform](/learning-paths/workers/devplat/intro-to-devplat/). +- Interacts with Cloudflare's [Developer Platform](/learning-paths/workers/devplat/intro-to-devplat/). - Requires [unit testing](/workers/testing/). -- Needs deployment automation via the CLI ([Wrangler](/workers/wrangler/)). +- Needs deployment automation via CLI ([Wrangler](/workers/wrangler/)). If your Snippet reaches the limits of execution time, memory, or functionality, transitioning to Workers ensures your logic can scale without restrictions. @@ -551,5 +566,6 @@ If your Snippet reaches the limits of execution time, memory, or functionality, Cloudflare Snippets provide a production-ready solution for fast, declarative edge traffic logic, bridging the gap between [Cloudflare Rules](/rules/) and [Developer Platform](/learning-paths/workers/devplat/intro-to-devplat/). Snippets and Workers solve different problems: + - Use Snippets for fast, lightweight traffic modifications at the edge, including header rewrites, caching, redirects, origin routing, custom responses, A/B testing and authentication. -- Workers are built for advanced compute, persistent state, and full-stack applications. \ No newline at end of file +- Workers are built for advanced compute, persistent state, and full-stack applications. From 9133f2fb3de1fd9032c49d1b02f217254c4b1d0d Mon Sep 17 00:00:00 2001 From: Pedro Sousa <680496+pedrosousa@users.noreply.github.com> Date: Mon, 17 Mar 2025 18:25:50 +0000 Subject: [PATCH 4/4] Small fixes --- src/content/docs/rules/snippets/when-to-use.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/docs/rules/snippets/when-to-use.mdx b/src/content/docs/rules/snippets/when-to-use.mdx index 6f7db604d12091..00fb178abc15ec 100644 --- a/src/content/docs/rules/snippets/when-to-use.mdx +++ b/src/content/docs/rules/snippets/when-to-use.mdx @@ -52,7 +52,7 @@ Snippets are ideal for fast, cost-free request and response modifications at the - Ultra-fast, edge-optimized execution, powered by [Ruleset Engine](/ruleset-engine/) and [Workers runtime](/workers/runtime-apis/). - Included at no additional cost on [all paid plans](/rules/snippets/#availability). -- Granular request matching using dozens of request attributes, such as [URI](/ruleset-engine/rules-language/fields/reference/http.request.full_uri/), [user-agent](/ruleset-engine/rules-language/fields/reference/http.user_agent/), and[cookies](/ruleset-engine/rules-language/fields/reference/http.request.cookies/). +- Granular request matching using dozens of request attributes, such as [URI](/ruleset-engine/rules-language/fields/reference/http.request.full_uri/), [user-agent](/ruleset-engine/rules-language/fields/reference/http.user_agent/), and [cookies](/ruleset-engine/rules-language/fields/reference/http.request.cookies/). - Sequential execution – multiple Snippets [can run](/rules/snippets/how-it-works/) on the same request, applying modifications step by step. - Native integration with [Cloudflare Rules](/rules/) – Snippets inherit request modifications from other products running in earlier [request phases](/ruleset-engine/reference/phases-list/#request-phases). - JavaScript and Web APIs support, including: @@ -78,7 +78,7 @@ Snippets are ideal for fast, cost-free request and response modifications at the | [Authenticate](/rules/snippets/examples/auth-with-headers/) requests, [pre-sign](/cache/interaction-cloudflare-products/waf-snippets/) URLs, run [A/B testing](/rules/snippets/examples/ab-testing-same-url/) | βœ… | βœ… | | Define logic using [JavaScript and Web APIs](/workers/languages/javascript/) | βœ… | βœ… | | Perform compute-heavy tasks (for example, [AI](/workers-ai/), [image transformations](/images/transform-images/transform-via-workers/)) | ❌ | βœ… | -| Store persistent data (for example, [KV](/kv/), [Durable Objects](/durable-objects/), [D1](/d1/)) | ❌ | βœ… | +| Store persistent data (for example, [KV](/kv/), [Durable Objects](/durable-objects/), and [D1](/d1/)) | ❌ | βœ… | | Build [APIs](/d1/tutorials/build-a-comments-api/) and [full-stack applications](/pages/framework-guides/deploy-an-astro-site/#video-tutorial) | ❌ | βœ… | | Use TypeScript, Python, Rust, or other programming [languages](/workers/languages/) | ❌ | βœ… | | Support non-HTTP [protocols](/workers/reference/protocols/) | ❌ | βœ… |