Our site is undergoing maintenance. Check back soon!
+ + + `, + { status: 503, headers: { "Content-Type": "text/html" } }, + ); + }, +}; +``` + +### Custom cache + +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; + }, +}; +``` + +### Redirect based on country code + +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); + }, +}; +``` + +### Redirect 403 Forbidden 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; + } + }, +}; +``` + +### Retry 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; + }, +}; +``` + +### Remove fields from API response + +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; + } + }, +}; +``` + +### Set CORS headers + +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 +}; + +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 + +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; + } + }, +}; +``` + +### Slow down 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; + }, +}; +``` + +--- + +## 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: 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); + }, +}; +``` + +#### 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); + + 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: 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; + }, +}; +``` + +#### 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; + }, +}; +``` + +**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. + +--- + +## 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/) + - [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 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.