INTEGRITY Документация

Кеширование с помощью fetch

Если вы хотите быстро начать, нажмите на кнопку ниже.

Deploy to Cloudflare

Это создаёт репозиторий в вашем аккаунте 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_response
use 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)
}

Кеширование HTML-ресурсов

// Force Cloudflare to cache an asset
fetch(event.request, { cf: { cacheEverything: true } });

Настройка уровня кеширования на Cache Everything переопределяет кешируемость ресурса по умолчанию. Что касается времени жизни (TTL), Cloudflare по-прежнему полагается на заголовки, установленные источником.

Custom cache keys

Cache key запроса определяет, считаются ли два запроса одинаковыми с точки зрения кэширования. Если у запроса тот же cache key, что и у предыдущего запроса, Cloudflare может отдать для обоих один и тот же закэшированный ответ. Подробнее о cache key см. Создание пользовательских ключей кеша документация.

// 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;

Workers, работающие от имени разных зон, не могут влиять на кэш друг друга. Переопределять ключи кэша можно только при запросах в пределах собственной зоны (в примере выше event.request.url хранился ключ), или запросы к хостам, не подключённым к Cloudflare. При обращении к другой зоне Cloudflare (например, принадлежащей другому клиенту Cloudflare) эта зона полностью управляет кешированием своего контента в Cloudflare, и вы не можете это изменить.

Кеширование ожидаемых ответов Vary

Используйте cf.vary когда источник возвращает Vary заголовок, и вы хотите, чтобы подзапрос Worker кэшировал ожидаемые варианты. Эта настройка применяется только к fetch() запрос, в котором вы его задали.

Подробнее о поведении Vary см. в Vary. Полный объект инициализации запроса см. в cf.vary.

src/index.js
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"],
						},
					},
				},
			},
		});
	},
};
src/index.ts
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 кэширует его на указанное время и переопределяет директивы кэширования, отправленные источником. Вы можете просмотреть подробнее о cacheTtl функция на странице Request.

Настроить поведение кеша в зависимости от типа файла запроса

Используя собственные ключи кеша и переопределения на основе кода ответа, можно написать Worker, который задаёт TTL в зависимости от кода статуса ответа источника и типа запрашиваемого файла.

Следующий пример показывает, как это можно использовать для кэширования запросов к потоковым медиафайлам:

index.js
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;
	},
};
index.js
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;
}

Использование HTTP Cache API

cache режим можно задать в fetch опции. В настоящее время Workers поддерживает только no-store и no-cache режим управления кэшем. Когда no-store указан, кеш пропускается на пути к источнику, и запрос не подлежит кешированию. Если no-cache указан, кеш принудительно выполняет повторную проверку текущего кешированного ответа с источником.

fetch(request, { cache: 'no-store'});
fetch(request, { cache: 'no-cache'});