如需快速上手,请点击下方按钮。
这会在你的 GitHub 账户中创建仓库,并将应用部署到 Cloudflare Workers。
export default {
async fetch(request) {
const url = new URL(request.url);
// Only use the path for the cache key, removing query strings
// and always store using HTTPS, for example, https://www.example.com/file-uri-here
const someCustomKey = `https://${url.hostname}${url.pathname}`;
let response = await fetch(request, {
cf: {
// Always cache this fetch regardless of content type
// for a max of 5 seconds before revalidating the resource
cacheTtl: 5,
cacheEverything: true,
//Enterprise only feature, see Cache API for other plans
cacheKey: someCustomKey,
},
});
// Reconstruct the Response object to make its headers mutable.
response = new Response(response.body, response);
// Set cache control headers to cache on browser for 25 minutes
response.headers.set("Cache-Control", "max-age=1500");
return response;
},
};export default {
async fetch(request): Promise<Response> {
const url = new URL(request.url);
// Only use the path for the cache key, removing query strings
// and always store using HTTPS, for example, https://www.example.com/file-uri-here
const someCustomKey = `https://${url.hostname}${url.pathname}`;
let response = await fetch(request, {
cf: {
// Always cache this fetch regardless of content type
// for a max of 5 seconds before revalidating the resource
cacheTtl: 5,
cacheEverything: true,
//Enterprise only feature, see Cache API for other plans
cacheKey: someCustomKey,
},
});
// Reconstruct the Response object to make its headers mutable.
response = new Response(response.body, response);
// Set cache control headers to cache on browser for 25 minutes
response.headers.set("Cache-Control", "max-age=1500");
return response;
},
} satisfies ExportedHandler;import { Hono } from 'hono';
type Bindings = {};
const app = new Hono<{ Bindings: Bindings }>();
app.all('*', async (c) => {
const url = new URL(c.req.url);
// Only use the path for the cache key, removing query strings
// and always store using HTTPS, for example, https://www.example.com/file-uri-here
const someCustomKey = `https://${url.hostname}${url.pathname}`;
// Fetch the request with custom cache settings
let response = await fetch(c.req.raw, {
cf: {
// Always cache this fetch regardless of content type
// for a max of 5 seconds before revalidating the resource
cacheTtl: 5,
cacheEverything: true,
// Enterprise only feature, see Cache API for other plans
cacheKey: someCustomKey,
},
});
// Reconstruct the Response object to make its headers mutable
response = new Response(response.body, response);
// Set cache control headers to cache on browser for 25 minutes
response.headers.set("Cache-Control", "max-age=1500");
return response;
});
export default app;from workers import WorkerEntrypoint, Response, fetch
from urllib.parse import urlparse
class Default(WorkerEntrypoint):
async def fetch(self, request):
url = urlparse(request.url)
# Only use the path for the cache key, removing query strings
# and always store using HTTPS, for example, https://www.example.com/file-uri-here
some_custom_key = f"https://{url.hostname}{url.path}"
response = await fetch(
request,
cf={
# Always cache this fetch regardless of content type
# for a max of 5 seconds before revalidating the resource
"cacheTtl": 5,
"cacheEverything": True,
# Enterprise only feature, see Cache API for other plans
"cacheKey": some_custom_key,
},
)
# Reconstruct the Response object to make its headers mutable
new_response = Response(response.body, headers=dict(response.headers))
# Set cache control headers to cache on browser for 25 minutes
new_response.headers["Cache-Control"] = "max-age=1500"
return new_responseuse worker::*;
#[event(fetch)]
async fn fetch(req: Request, _env: Env, _ctx: Context) -> Result<Response> {
let url = req.url()?;
// Only use the path for the cache key, removing query strings
// and always store using HTTPS, for example, https://www.example.com/file-uri-here
let custom_key = format!(
"https://{host}{path}",
host = url.host_str().unwrap(),
path = url.path()
);
let request = Request::new_with_init(
url.as_str(),
&RequestInit {
headers: req.headers().clone(),
method: req.method(),
cf: CfProperties {
// Always cache this fetch regardless of content type
// for a max of 5 seconds before revalidating the resource
cache_ttl: Some(5),
cache_everything: Some(true),
// Enterprise only feature, see Cache API for other plans
cache_key: Some(custom_key),
..CfProperties::default()
},
..RequestInit::default()
},
)?;
let mut response = Fetch::Request(request).send().await?;
// Set cache control headers to cache on browser for 25 minutes
let _ = response.headers_mut().set("Cache-Control", "max-age=1500");
Ok(response)
}// Force Cloudflare to cache an asset
fetch(event.request, { cf: { cacheEverything: true } });将缓存级别设置为 Cache Everything(全部缓存) 会覆盖资源的默认可缓存性。对于 TTL(生存时间),Cloudflare 仍会依赖源站设置的标头。
请求的缓存键决定两个请求在缓存目的上是否相同。如果某请求与之前的请求具有相同的缓存键,Cloudflare 可以为两者提供相同的缓存响应。有关缓存键的更多信息,请参阅创建自定义缓存键文档。
// Set cache key for this request to "some-string".
fetch(event.request, { cf: { cacheKey: "some-string" } });通常,Cloudflare 根据请求的 URL 计算缓存键。但有时你可能希望不同的 URL 在缓存时被同等对待。例如,如果你的网站内容同时托管在 Amazon S3 和 Google Cloud Storage 上——两处内容相同,你可以用 Worker 在两者之间随机负载均衡。但你不想缓存两份内容副本。你可以利用自定义缓存键,基于原始请求 URL 而非子请求 URL 进行缓存:
export default {
async fetch(request) {
let url = new URL(request.url);
if (Math.random() < 0.5) {
url.hostname = "example.s3.amazonaws.com";
} else {
url.hostname = "example.storage.googleapis.com";
}
let newRequest = new Request(url, request);
return fetch(newRequest, {
cf: { cacheKey: request.url },
});
},
};export default {
async fetch(request): Promise<Response> {
let url = new URL(request.url);
if (Math.random() < 0.5) {
url.hostname = "example.s3.amazonaws.com";
} else {
url.hostname = "example.storage.googleapis.com";
}
let newRequest = new Request(url, request);
return fetch(newRequest, {
cf: { cacheKey: request.url },
});
},
} satisfies ExportedHandler;import { Hono } from 'hono';
type Bindings = {};
const app = new Hono<{ Bindings: Bindings }>();
app.all('*', async (c) => {
const originalUrl = c.req.url;
const url = new URL(originalUrl);
// Randomly select a storage backend
if (Math.random() < 0.5) {
url.hostname = "example.s3.amazonaws.com";
} else {
url.hostname = "example.storage.googleapis.com";
}
// Create a new request to the selected backend
const newRequest = new Request(url, c.req.raw);
// Fetch using the original URL as the cache key
return fetch(newRequest, {
cf: { cacheKey: originalUrl },
});
});
export default app;代表不同 zone 运行的 Workers 无法影响彼此的缓存。你只能在使用自己的 zone 发起请求时(上例中存储的键为 event.request.url),或对不在 Cloudflare 上的主机发起请求时,覆盖缓存键。向另一个 Cloudflare zone 发起请求时(例如属于不同 Cloudflare 客户的 zone),该 zone 完全控制其内容在 Cloudflare 内的缓存方式;你无法覆盖它。
当源站返回 Vary 标头且你希望 Worker 子请求缓存预期变体时,使用 cf.vary。此设置仅适用于你设置它的 fetch() 请求。
有关 Vary 行为的详情,请参阅 Vary。有关完整请求 init 对象,请参阅 cf.vary。
export default {
async fetch(request) {
return fetch(request, {
cf: {
vary: {
default: { action: "bypass" },
headers: {
accept: {
action: "normalize",
media_types: ["text/html", "application/json"],
},
"accept-language": {
action: "normalize",
languages: ["en", "fr", "de"],
},
},
},
},
});
},
};export default {
async fetch(request): Promise<Response> {
return fetch(request, {
cf: {
vary: {
default: { action: "bypass" },
headers: {
accept: {
action: "normalize",
media_types: ["text/html", "application/json"],
},
"accept-language": {
action: "normalize",
languages: ["en", "fr", "de"],
},
},
},
},
});
},
} satisfies ExportedHandler;// Force response to be cached for 86400 seconds for 200 status
// codes, 1 second for 404, and do not cache 500 errors.
fetch(request, {
cf: { cacheTtlByStatus: { "200-299": 86400, 404: 1, "500-599": 0 } },
});此选项是 cacheTtl 功能的变体,根据响应的状态码选择 TTL,且不会自动设置 cacheEverything: true。如果此请求的响应状态码匹配,Cloudflare 将按指示的时间缓存,并覆盖源站发送的缓存指令。你可以在 Request 页面 查看 cacheTtl 功能的详情。
结合自定义缓存键和基于响应码的覆盖,你可以编写 Worker,根据来自源站的响应状态码和请求文件类型设置 TTL。
以下示例演示如何将此用于缓存流媒体资源请求:
export default {
async fetch(request) {
// Instantiate new URL to make it mutable
const newRequest = new URL(request.url);
const customCacheKey = `${newRequest.hostname}${newRequest.pathname}`;
const queryCacheKey = `${newRequest.hostname}${newRequest.pathname}${newRequest.search}`;
// Different asset types usually have different caching strategies. Most of the time media content such as audio, videos and images that are not user-generated content would not need to be updated often so a long TTL would be best. However, with HLS streaming, manifest files usually are set with short TTLs so that playback will not be affected, as this files contain the data that the player would need. By setting each caching strategy for categories of asset types in an object within an array, you can solve complex needs when it comes to media content for your application
const cacheAssets = [
{
asset: "video",
key: customCacheKey,
regex:
/(.*\/Video)|(.*\.(m4s|mp4|ts|avi|mpeg|mpg|mkv|bin|webm|vob|flv|m2ts|mts|3gp|m4v|wmv|qt))/,
info: 0,
ok: 31556952,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "image",
key: queryCacheKey,
regex:
/(.*\/Images)|(.*\.(jpg|jpeg|png|bmp|pict|tif|tiff|webp|gif|heif|exif|bat|bpg|ppm|pgn|pbm|pnm))/,
info: 0,
ok: 3600,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "frontEnd",
key: queryCacheKey,
regex: /^.*\.(css|js)/,
info: 0,
ok: 3600,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "audio",
key: customCacheKey,
regex:
/(.*\/Audio)|(.*\.(flac|aac|mp3|alac|aiff|wav|ogg|aiff|opus|ape|wma|3gp))/,
info: 0,
ok: 31556952,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "directPlay",
key: customCacheKey,
regex: /.*(\/Download)/,
info: 0,
ok: 31556952,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "manifest",
key: customCacheKey,
regex: /^.*\.(m3u8|mpd)/,
info: 0,
ok: 3,
redirects: 2,
clientError: 1,
serverError: 0,
},
];
const { asset, regex, ...cache } =
cacheAssets.find(({ regex }) => newRequest.pathname.match(regex)) ?? {};
const newResponse = await fetch(request, {
cf: {
cacheKey: cache.key,
polish: false,
cacheEverything: true,
cacheTtlByStatus: {
"100-199": cache.info,
"200-299": cache.ok,
"300-399": cache.redirects,
"400-499": cache.clientError,
"500-599": cache.serverError,
},
cacheTags: ["static"],
},
});
const response = new Response(newResponse.body, newResponse);
// For debugging purposes
response.headers.set("debug", JSON.stringify(cache));
return response;
},
};addEventListener("fetch", (event) => {
return event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
// Instantiate new URL to make it mutable
const newRequest = new URL(request.url);
// Set `const` to be used in the array later on
const customCacheKey = `${newRequest.hostname}${newRequest.pathname}`;
const queryCacheKey = `${newRequest.hostname}${newRequest.pathname}${newRequest.search}`;
// Set all variables needed to manipulate Cloudflare's cache using the fetch API in the `cf` object. You will be passing these variables in the objects down below.
const cacheAssets = [
{
asset: "video",
key: customCacheKey,
regex:
/(.*\/Video)|(.*\.(m4s|mp4|ts|avi|mpeg|mpg|mkv|bin|webm|vob|flv|m2ts|mts|3gp|m4v|wmv|qt))/,
info: 0,
ok: 31556952,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "image",
key: queryCacheKey,
regex:
/(.*\/Images)|(.*\.(jpg|jpeg|png|bmp|pict|tif|tiff|webp|gif|heif|exif|bat|bpg|ppm|pgn|pbm|pnm))/,
info: 0,
ok: 3600,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "frontEnd",
key: queryCacheKey,
regex: /^.*\.(css|js)/,
info: 0,
ok: 3600,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "audio",
key: customCacheKey,
regex:
/(.*\/Audio)|(.*\.(flac|aac|mp3|alac|aiff|wav|ogg|aiff|opus|ape|wma|3gp))/,
info: 0,
ok: 31556952,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "directPlay",
key: customCacheKey,
regex: /.*(\/Download)/,
info: 0,
ok: 31556952,
redirects: 30,
clientError: 10,
serverError: 0,
},
{
asset: "manifest",
key: customCacheKey,
regex: /^.*\.(m3u8|mpd)/,
info: 0,
ok: 3,
redirects: 2,
clientError: 1,
serverError: 0,
},
];
// the `.find` method is used to find elements in an array (`cacheAssets`), in this case, `regex`, which can passed to the .`match` method to match on file extensions to cache, since they are many media types in the array. If you want to add more types, update the array. Refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find for more information.
const { asset, regex, ...cache } =
cacheAssets.find(({ regex }) => newRequest.pathname.match(regex)) ?? {};
const newResponse = await fetch(request, {
cf: {
cacheKey: cache.key,
polish: false,
cacheEverything: true,
cacheTtlByStatus: {
"100-199": cache.info,
"200-299": cache.ok,
"300-399": cache.redirects,
"400-499": cache.clientError,
"500-599": cache.serverError,
},
cacheTags: ["static"],
},
});
const response = new Response(newResponse.body, newResponse);
// For debugging purposes
response.headers.set("debug", JSON.stringify(cache));
return response;
}可以在 fetch 选项中设置 cache 模式。
目前 Workers 仅支持 no-store 和 no-cache 模式来控制缓存。
传入 no-store 时,缓存在前往源站途中被绕过,且请求不可缓存。
传入 no-cache 时,缓存会强制用源站重新验证当前缓存的响应。
fetch(request, { cache: 'no-store'});
fetch(request, { cache: 'no-cache'});