К основному содержанию
скилл CX Codex

netlify-caching — скилл для Claude Code: что делает и как установить

Скилл помогает настроить кеширование ответов на CDN Netlify для Functions и Edge Functions. Пригодится при работе с заголовками кеша, тегами и сбросом данных.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add netlify/context-and-tools --skill netlify-caching -a claude-code
Открыть на GitHub

Что делает netlify-caching

Netlify не кеширует динамические ответы функций по умолчанию. Скилл обучает агента правильно выставлять заголовок Netlify-CDN-Cache-Control, использовать директивы s-maxage и stale-while-revalidate, а также управлять разделением ключей через заголовок Netlify-Vary по параметрам запроса, стране или cookie.

Внутри собраны правила программной очистки кеша через метод purgeCache из @netlify/functions и прямой вызов Netlify API. Скилл объясняет, как группировать ответы с помощью Netlify-Cache-Tag и защищать динамические ответы от сброса при деплое через Netlify-Cache-ID.

Учитывайте ограничения платформы: включение basic-auth даже на одной странице отключает кеширование для всего сайта. Директива durable не работает в Edge Functions, а встроенный Cache API молча отбрасывает любые ответы тяжелее 10 МБ.

Какие задачи решает

  • настроить заголовок Netlify-CDN-Cache-Control для serverless-функции
  • разделить кеш по странам и cookie с помощью Netlify-Vary
  • очистить кеш по тегам через purgeCache из @netlify/functions
  • исключить ответы из автоматической инвалидации при деплое через Netlify-Cache-ID

Примеры запросов агенту

  • Настрой кеширование для Netlify Function с директивой stale-while-revalidate на 2 минуты
  • Добавь заголовок Netlify-Vary для разделения кеша по cookie и стране
  • Напиши функцию сброса кеша по тегу с использованием purgeCache

Как установить netlify-caching

1Через Skills CLI (Vercel)

npx skills add netlify/context-and-tools --skill netlify-caching -a claude-code

2Вручную: положить файлы

~/.claude/skills/netlify-caching/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/netlify-caching/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора14

netlify-config Настраивает сборку и маршрутизацию в Netlify через netlify.toml, _redirects и _headers. Пригодится для роутинга SPA, прокси и контекстов деплоя. CX Codex +1 3 300установок netlify-deploy Скилл ведёт агента по деплоям Netlify: настройка Git CD, netlify.toml, Deploy Previews, build hooks и разбор упавших сборок. CX Codex +1 3 400установок netlify-frameworks Скилл подсказывает агенту настройки сборки, SSR-адаптеры и переменные окружения для деплоя веб-фреймворков на Netlify. CX Codex +1 3 200установок netlify-functions Помогает агенту писать и настраивать серверless-функции Netlify на TypeScript, JavaScript или Go: маршруты, cron, лимиты, чтение секретов. CX Codex +1 3 200установок netlify-forms Скилл для работы с серверлес-формами на Netlify: разметка, AJAX-отправка, защита от спама, уведомления. Пригодится, когда форма молча не регистрируется. CX Codex +1 3 100установок netlify-edge-functions Помогает писать, роутить и отлаживать edge-функции Netlify на Deno: middleware, редиректы по гео, кэш, стриминг SSR. CX Codex +1 3 100установок netlify-mcp-servers Скилл помогает развернуть собственный MCP-сервер на бессерверной функции Netlify. Пригодится, когда нужно дать агенту доступ к API или данным проекта через HTTP. CX Codex +1 3 000установок netlify-identity Скилл для добавления входа, регистрации и OAuth на сайт Netlify через @netlify/identity. Пригодится, когда нужно закрыть контент по ролям. CX Codex +1 3 000установок

Похожие

netlify-development Mindrally Скилл с правилами сборки и деплоя на Netlify: serverless- и edge-функции, Blobs, Image CDN, конфигурация сборки. Пригодится, когда агент пишет функции или правит netlify.toml. CX Codex +1 665установок netlify-official-cursorrules-prompt-file PatrickJS Правило для Cursor, которое задаёт агенту официальные соглашения Netlify: типы функций, работа с окружением и запрет лишних CORS-заголовков. ★ 41 011звёзд репо cdn-caching официальный Vercel Labs Помогает понять, почему страница отдаётся из кэша или мимо него: статусы, причины, ревалидация, стоимость ISR. CX Codex GP ChatGPT 2установок firebase-hosting-basics Google LLC Разворачивает статические сайты и SPA на Firebase Hosting CDN с SSL, preview-каналами и кастомными доменами. CX Codex +1 170 300установок setup-cdn-optimization davila7 Слэш-команда разбирает трафик и типы контента, затем настраивает CDN: кэш, сжатие, картинки, инвалидацию. 218установок llm-caching devops-skills Настраивает многоуровневое кэширование запросов к нейросетям через Redis, GPTCache и провайдерские API. Снижает затраты на 30–70% при частых повторных вопросах. CX Codex +1 317установок

Рядом в теме «Бэкенд и API»

Всё по теме «Бэкенд и API» →

Вопросы о netlify-caching

Как установить netlify-caching в Claude Code?

Выполните команду npx skills add netlify/context-and-tools --skill netlify-caching -a claude-code.

Что делает netlify-caching?

Скилл даёт правила и примеры для настройки кеширования динамических ответов на CDN Netlify, разделения кеш-ключей и точечного сброса по тегам.

Почему basic-auth ломает кеширование на Netlify?

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

Исходный текст

SKILL.md · MIT
---
name: netlify-caching
description: Cache dynamic and static responses on Netlify's CDN from Functions, Edge Functions, and proxies. Use when you add caching or cache-control headers to a function response, tune cache TTL or stale-while-revalidate, set up the durable cache, vary a cache key by query/header/cookie/country/language, purge or invalidate the cache by site or cache tag, use the programmatic Cache API (caches.open/match/put) or @netlify/cache helpers (fetchWithCache/cacheHeaders/getCacheStatus), speed up an expensive API call, add ISR or on-demand revalidation, or debug why a response is or isn't cached via the Cache-Status header.
---

# Netlify caching

## Cache-control header to reach for

Dynamic responses (Functions, Edge Functions, proxies) are **NOT cached by default**. You must opt in. Set `Netlify-CDN-Cache-Control` on the response:

```ts
import type { Context } from "@netlify/functions";

export default async (req: Request, context: Context) => {
  return new Response("Hello world", {
    headers: {
      'Netlify-CDN-Cache-Control': 'public, durable, max-age=60, stale-while-revalidate=120'
    }
  });
};
```

Header choice (most specific wins; `CDN-Cache-Control`/`Cache-Control` always pass downstream):
- `Netlify-CDN-Cache-Control` applies to the Netlify CDN only. **Reach for this.**
- `CDN-Cache-Control` applies to all CDNs that support it.
- `Cache-Control` applies to any CDN or the browser.

**Legacy path to avoid:** On-demand Builders do **not** support these headers or `Netlify-Vary`. They use a TTL pattern and key on URL path only. Don't reach for ODBs in new code.

## Footguns (read first)

- **Static assets are fresh for up to a year.** A shorter `max-age` is ignored. They change only on a new deploy or manual purge.
- **basic-auth on ANY page disables caching for the ENTIRE site.**
- **`durable` is serverless-only.** It has no effect on Edge Function responses.
- **Cache API silently drops responses over 10 MB.** `cache.put()` doesn't store them and later lookups miss. Compress the content or split it across multiple entries.
- Never opt sensitive content out of automatic invalidation. It can stay publicly cached after deploys/firewall changes.

## Directives

- `public` caches the response. `private` caches it in the browser only, not in Netlify's shared cache. `no-store` prevents caching.
- `s-maxage=N` sets seconds in Netlify's shared cache (overrides `max-age` there).
- `max-age=N` sets seconds in any cache.
- `stale-while-revalidate=N` serves stale for N seconds after expiry while revalidating in the background.
- `durable` (serverless only) stores the response in Netlify's durable cache so other edge nodes reuse it instead of re-invoking the function.

Defaults when no header is set:
- Static: `Netlify-CDN-Cache-Control: public, s-maxage=31536000, must-revalidate`
- Dynamic: `Cache-Control: public, max-age=0, must-revalidate`

## Cache key variation — `Netlify-Vary`

Comma-delimited instructions on the response; pipe-delimited value lists:

```
Netlify-Vary: query=item_id|page, country=es+de|us, cookie=ab_test|is_logged_in
```

- `query=a|b` varies on a subset; bare `query` varies on all params. Keys are case-sensitive; param order is irrelevant.
- `header=Device-Type|App-Version` supports custom and most standard headers.
- `language=en|es+pt` uses `+` for groups. It is checked against `Accept-Language` with quality weighting.
- `country=us|es+pt` uses GeoIP with ISO 3166-1 two-letter codes; `+` groups.
- `cookie=ab_test|is_logged_in` targets specific keys, not the whole `Cookie` header.

**Cannot vary by header on:** `Accept*`, `Cache-Control`, `Connection`, `Content-Length`, `Cookie`, `Host`, `If-*`, `Range`, `Referer`, `Upgrade`, `User-Agent`. For language/cookie/format, use `Vary: Accept-Language`/`Vary: Cookie` or the specific `Netlify-Vary` instruction.

**Consistency rule:** a URL must return the same `Netlify-Vary` on every response. The first cached response's instructions win and later ones are ignored.

`Netlify-Vary` and standard `Vary` are both respected. Use `Vary` for format/encoding, and to pass instructions to an upstream CDN like Cloudflare.

## Cache tags & opt-out

Tag responses for taggable purging:

```
Netlify-Cache-Tag: tag1,tag2,tag3
```

- `Netlify-Cache-Tag` (Netlify CDN) wins over `Cache-Tag` (passed downstream). Some providers strip `Cache-Tag`, so set both when proxying through them.
- Constraints: case-insensitive, UTF-8 only, ≤1024 chars/tag, ≤500 tags/response.

Opt a response out of automatic atomic-deploy invalidation with `Netlify-Cache-ID`. Values are comma-separated, auto-registered as cache tags for purging, and have a separate 500-ID limit:

```
Netlify-Cache-ID: cms-proxy,product,image
```

After opting out, purge on demand after relevant changes, e.g. redirect/proxy or function changes behind a `Netlify-Cache-ID`.

## On-demand invalidation (purge)

Purge from a **deployed function** with `purgeCache`. The site ID is passed automatically:

```ts
import { purgeCache } from "@netlify/functions";

export default async () => {
  await purgeCache(); // no args = purge everything for the site
  return new Response("Purged!", { status: 202 });
};
```

Purge by tag, optionally targeting a deploy/subdomain:

```ts
import { purgeCache } from "@netlify/functions";

export default async (req: Request) => {
  const cacheTag = new URL(req.url).searchParams.get("tag");
  if (!cacheTag) return;
  await purgeCache({
    tags: [cacheTag],
    deployAlias: "deploy-preview-11",
    domain: "early-access.company.com",
  });
  return new Response("Purged!", { status: 202 });
};
```

**Lambda-compatible functions** use the legacy `module.exports.handler = async (event, context) => {…}` signature. They must pass `context.clientContext.custom.purge_api_token`:

```ts
import { purgeCache } from "@netlify/functions";

module.exports.handler = async (event, context) => {
  const token = context.clientContext.custom.purge_api_token;
  await purgeCache({ tags: ["tag1", "tag2"], token });
  return { body: "Purged!", statusCode: 202 };
};
```

Direct API (from outside a function): `POST https://api.netlify.com/api/v1/purge` with `Authorization: Bearer <personal_access_token>` and `Content-Type: application/json`:

```sh
curl -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <personal_access_token>" \
  --data '{"site_slug": "mysitename", "cache_tags": ["news"], "deploy_alias": "deploy-preview-11", "domain": "early-access.company.com"}' \
  'https://api.netlify.com/api/v1/purge'
```

- Purge by site: pass `site_id` or `site_slug`.
- Purge by tag: pass `cache_tags` plus the site.
- Omitting `cache_tags` purges the whole site. An **empty** `cache_tags` list purges NOTHING.
- Identifier mapping: in the UI (Project configuration > General > Project details > Project information), **Project ID** = `site_id` and **Project name** = `site_slug`. See https://docs.netlify.com/api-and-cli-guides/api-guides/get-started-with-api#get-site.
- **Rate limit:** each tag or site can be purged only twice per 5s. Exceeding it returns `429`.

## Cache API (`caches` global)

Programmatic read/write of HTTP responses from Functions/Edge Functions. Use it to cache individual components of a route or arbitrary fetches, alongside header-based route caching.

**Scope rule:** call `caches.open()` anywhere, but call `match`/`put`/`delete` **only inside the request handler**. Doing them at module/global scope throws.

```ts
import type { Config, Context } from "@netlify/functions";

const cache = await caches.open("my-cache"); // ok in global scope

export default async (req: Request, context: Context) => {
  const request = new Request("https://example.com/expensive-api");
  const cached = await cache.match(request);
  if (cached) return cached;

  const fresh = await fetch(request);
  if (fresh.ok) {
    cache.put(request, fresh.clone()).catch((error) => {
      console.error("Failed to add to the cache:", error);
    });
  }
  return fresh;
};

export const config: Config = { path: "/cache-api-example" };
```

`CacheStorage` subset:
- `caches.match(request)` returns a `Response` from any cache, or `undefined`.
- `caches.open(name)` returns a `Cache`. Distinct names fragment the cache and lower the hit ratio, so use few, meaningful names.

`Cache` methods (all require `caches.open()`):
- `cache.match(request)` returns a `Response` or `undefined`.
- `cache.put(request, response)` adds a response.
- `cache.add(request)` / `cache.addAll(requests)` fetch and store.
- `cache.delete(request)` returns `true`.
- `keys()` is **not implemented**, so there is no way to list contents.

Consistency: reads/writes are strongly consistent. **Deletes are eventually consistent**, so a deleted entry may still return briefly.

**Cannot cache:**
- Partial responses (206), `Vary: *`, or non-`GET` methods.
- Responses over **10 MB**. These are silently not stored.

Responses also need all of the following, otherwise storage errors:
- a cache-control header with `max-age`/`s-maxage` ≥ 1s
- `public` (not `private`/`no-cache`/`no-store`)
- a 2xx status

For responses you don't control, rewrite headers with `fetchWithCache`.

**Limits per invocation:** 100 lookups, 20 insertions/deletions. Beyond these, further lookups return nothing and writes/deletes no-op. Limits are shared across edge functions in a request, but separate between serverless and edge functions.

Cache data is per-region (not replicated). It is auto-invalidated on redeploy and on `max-age`/`s-maxage` expiry.

## `@netlify/cache` module

Install to get helpers, time constants (`MINUTE`/`HOUR`/`DAY`), and a `caches` export for local dev:

```
npm install @netlify/cache
```

**Local-dev workaround:** the `caches` global isn't part of Node.js. Netlify provides it in its Functions/Edge runtimes, both live and under `netlify dev`. If you run your framework's own dev server, the global is undefined and throws. Import it instead:

```ts
import { caches } from "@netlify/cache";
const cache = await caches.open("my-cache");
```

This requires Netlify CLI 20.0.3+. Nothing persists locally: lookups return nothing, and writes/deletes don't mutate. There is no functional change from the global.

### `cacheHeaders(settings)` → header object

```ts
import { cacheHeaders, DAY } from "@netlify/cache";

const headers = {
  "x-custom-header": "some value",
  ...cacheHeaders({
    ttl: 2 * DAY,          // s-maxage
    swr: HOUR,             // stale-while-revalidate
    durable: true,
    tags: ["product", "sale"],
    overrideDeployRevalidation: ["tag"], // opt out of atomic-deploy invalidation
    vary: {
      cookie: ["ab_test_name", "ab_test_bucket"],
      query: ["item_id", "page"], // or true for all
      country: ["us", ["es", "pt"]], // nested = OR
      language: ["en"],
      header: ["Device-Type"],
    },
  }),
};
```

For only generic (non-Netlify) headers, use the `cdn-cache-control` npm module instead.

### `fetchWithCache(resource, options?, cacheSettings?)`

Drop-in `fetch` that returns a cached response, or fetches, stores, and returns. `cacheSettings` override conflicting response headers. With `swr`, background revalidation is handled automatically.

```ts
import { fetchWithCache, DAY } from "@netlify/cache";

const response = await fetchWithCache("https://example.com/expensive-api", {
  ttl: 2 * DAY,
  tags: ["product", "sale"],
  vary: { cookie: ["ab_test_name"], query: ["item_id", "page"] },
});
```

### `getCacheStatus(response | headers | headerString)`

Returns `{ hit, caches: { durable: { hit, stale, stored, ttl }, edge: { hit, stale } } }`.

```ts
const { hit, edge, durable } = getCacheStatus(response);
```

### `needsRevalidation(response)` → boolean

Only needed when calling `cache.match`/`cache.put` directly (not with `fetchWithCache` + `swr`). It is true when a Cache-API response is stale within its SWR window. Return the stale response, then revalidate in `context.waitUntil` and `cache.put` the fresh copy:

```ts
if (cached) {
  if (needsRevalidation(cached)) {
    context.waitUntil(
      fetch(request).then((fresh) => {
        const response = new Response(fresh.body, {
          headers: { ...Object.fromEntries(fresh.headers), ...cacheHeaders({ ttl: MINUTE, swr: HOUR }) },
        });
        return cache.put(request, response);
      })
    );
  }
  return cached;
}
```

## Durable cache

Add `durable` (serverless only) so edge nodes lacking a local copy check the shared durable cache before invoking the function. This means fewer invocations and better cache-miss latency.

- It is eventually consistent, so multiple regions may still invoke the function a few times per version.
- It is co-located with the site's functions region.
- It works with `Netlify-Vary`, SWR, and on-demand invalidation.
- **Next.js:** Next Runtime 5.5.0+ uses the durable cache automatically.

## Debugging with `Cache-Status`

Netlify sets `Cache-Status` (RFC 9211) on all responses. Check it on a **deployed** URL. Look for values starting with `"Netlify Edge"` or `"Netlify Durable"`:

- `"Netlify Edge"; fwd=miss` means nothing was cached.
- `"Netlify Edge"; hit` means the response was served from cache.
- `"Netlify Edge"; hit; fwd=stale` means a stale response was served while revalidating (SWR).
- `"Netlify Durable"; fwd=uri-miss; stored=true; ttl=3600` means a durable miss that was then stored.
- `"Netlify Durable"; hit; ttl=1234` means a durable hit.

A negative `ttl` is the number of seconds since expiry. Each request may hit a different cache instance. Without production traffic or `durable`, expect several empty caches before a hit; repeat requests to warm one.

<!-- Gaps: @netlify/cache local-dev docs show cache.set; resolved to the documented cache.put. 10 MB limit doesn't say compressed vs uncompressed size, or whether it covers add/addAll/fetchWithCache. -->

<!-- system: agent-context/caching/system.md — human-owned, merged by ctx-gen; edit system.md, not this section -->
# Netlify house rules (caching)

These are org conventions, not docs facts — merged into the rendered skill by
ctx-gen and never generated. Owned by the skills maintainer.

1. Only `GET` responses are cached by the CDN. `POST`/`PUT`/etc. are never
   cached regardless of headers — expose cacheable data on a `GET` route
   (put the inputs in the URL or query string).
2. Without `Netlify-Vary: query=...`, the full query string is the cache key —
   every distinct query string (`utm_*`, `fbclid`, ...) is a separate cache
   entry. Enumerate only the params that actually change the response.
3. `netlify dev` does not emulate the CDN cache — a cache miss every time
   locally is expected, not a bug. Verify caching behavior on a deployed URL
   (Deploy Preview or production) via its `Cache-Status` header.
4. `purgeCache()` has ambient credentials only inside a deployed function.
   From CI, local scripts, or the build, pass `token` (a personal access
   token read from an env var, never hardcoded) and `siteID`.

© netlify · MIT · netlify/context-and-tools

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой