本指南帮助您决定在 Cloudflare 的全球网络上何时使用 Snippets 或 Workers。它提供了最佳实践、比较和实际用例,以帮助您为您的工作负载选择合适的产品。
Cloudflare Snippets 提供了一种在边缘修改 HTTP 请求和响应的快速、声明式方法,无需完整的计算平台。Snippets 扩展了 Cloudflare Rules,允许您编写基于 JavaScript 的逻辑,在请求到达源站之前修改请求,并在响应从上游返回后修改响应。
Snippets 使您能够:
- 修改标头、验证 JWT 以及实施复杂的重写或重定向。
- 重试到不同源站的失败请求并应用自定义缓存策略。
- 顺序执行多个 Snippets,每个 Snippet 在将其传递给下一个之前修改请求或响应。
Snippets 包含在所有付费计划中,无需额外费用,是轻量级边缘逻辑的首选解决方案。
相比之下,Cloudflare Workers 提供了一个全栈计算平台,专为需要状态、计算以及与 Cloudflare Developer Platform 集成的应用程序而设计。Workers 采用基于使用量的定价模型,并包含一个免费层。
Snippets 是在边缘进行快速、免费的请求和响应修改的理想选择。它们扩展了 Cloudflare Rules,无需额外的基础设施或外部解决方案。
- 直接在 Cloudflare 网络上应用超快速的流量修改。
- 将 Cloudflare Rules 扩展到内置操作之外,以获得更大的控制权。
- 通过替换 VCL、EdgeWorkers 或本地逻辑来简化 CDN 迁移。
- 修改标头、缓存响应和执行重定向。
- 使用 JavaScript 将边缘逻辑集成到开发工作流程中。
- 持久状态管理(例如,会话存储或数据库)。
- 计算密集型任务(例如,图像转换或 AI 推理)。
- 与 Developer Platform 服务(如 Durable Objects 或 D1)的深度集成。
- 需要高级运行时功能的用例,例如:
- 超快的边缘优化执行,由 Ruleset Engine 和 Workers runtime 提供支持。
- 包含在所有付费计划中,无需额外费用。
- 使用数十个请求属性进行精细的请求匹配,例如 URI、user-agent 和 cookies。
- 顺序执行 – 多个 Snippets 可以在同一个请求上运行,逐步应用修改。
- 与 Cloudflare Rules 的原生集成 – Snippets 继承了在早期请求阶段 (request phases) 运行的其他产品的请求修改。
- 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 实际应用的实用用例。您可以在 Examples 部分找到更多帮助您入门的模板。
动态修改请求和响应标头。
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;
}
},
};调整跨域资源共享 (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 具有不同的功能,但它们可以协同工作以处理复杂的流量工作流程。
为避免冲突,Snippets 和 Workers 应该在单独的请求路径上运行,而不是在相同的 URL 上运行。让它们在其逻辑中作为子请求提取各自的 URL,以确保平滑的执行和缓存行为。
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,这意味着不依赖于绑定、持久存储或高级执行功能的 JavaScript 代码可以在它们之间无缝迁移。
如果 Worker 满足以下条件,您应该考虑将其迁移到 Snippets:
- 仅修改标头、重定向、缓存规则或源站路由。
- 不需要绑定、持久存储或外部集成。
- 是一个具有简单逻辑的轻量级 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 解决不同的问题:
- 使用 Snippets 在边缘进行快速、轻量级的流量修改,包括标头重写、缓存、重定向、源站路由、自定义响应、A/B 测试和身份验证。
- Workers 专为高级计算、持久状态和全栈应用程序而构建。