后端与接口
在 Netlify 上为 Functions/Edge…
当你要在 Netlify 上缓存动态/静态响应、调整缓存 TTL 或 stale-while-revalidate、按 query/header/cookie/国家/语言区分缓存、按站点或缓存标签失效、使用 Cache API 或 @netlify/cache 辅助函数,或排查响应为何被/未被缓存时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Netlify 上为 Functio特定
解决思路
- 动态响应(Functions、Edge Functions、代理)默认不缓存,必须在响应上设置 `Netlify-CDN-Cache-Control` 头来显式开启缓存。
- 按“最具体者优先”选择缓存头:优先用 `Netlify-CDN-Cache-Control`(仅作用于 Netlify CDN);需要所有支持该头的 CDN 时用 `CDN-Cache-Control`;需要任何 CDN 或浏览器时用 `Cache-Control`(`CDN-Cache-Control`/`Cache-Control` 总会向下游传递)。
- 新代码不要使用 On-demand Builders(ODB):它们不支持这些头或 `Netlify-Vary`,只用 TTL 模式且仅以 URL path 作为 key。
- 把可缓存数据只暴露在 GET 路由上(输入放在 URL 或 query string 中),因为只有 `GET` 会被缓存,POST/PUT 等无论头怎么写都不会被缓存。
- 在部署后的 URL(Deploy Preview 或生产)上通过 `Cache-Status` 头验证缓存行为,因为 `netlify dev` 不模拟 CDN 缓存,本地每次 miss 是预期现象。
- 用 `Netlify-Vary: query=...` 只枚举真正会改变响应的查询参数,否则整个 query string 都会成为缓存 key,每个 `utm_*`、`fbclid` 等都会产生独立缓存条目。
- 注意静态资源默认新鲜期最长可达一年,更短的 `max-age` 会被忽略,只有新部署或手动 purge 才会改变。
- 避免在任意页面上启用 basic-auth,因为它会禁用整个站点的缓存。
- 只在 serverless 函数响应上使用 `durable`,它对 Edge Function 响应无效。
- 不要对敏感内容关闭自动失效,否则部署或防火墙变更后它可能仍被公开缓存。
- 按需使用指令:`public` 表示可缓存;`private` 表示仅浏览器缓存、不进 Netlify 共享缓存;`no-store` 表示不缓存。
- 用 `s-maxage=N` 指定 Netlify 共享缓存中的秒数(在该缓存中覆盖 `max-age`),用 `max-age=N` 指定任意缓存中的秒数。
- 用 `stale-while-revalidate=N` 在过期后 N 秒内继续返回旧内容并在后台重验证。
- 在 serverless 响应上加 `durable`,把内容存入 Netlify durable cache,让其他边缘节点复用而不是重新调用函数。
- 记住未设置头时的默认值:静态资源为 `Netlify-CDN-Cache-Control: public, s-maxage=31536000, must-revalidate`,动态响应为 `Cache-Control: public, max-age=0, must-revalidate`。
- 用 `Netlify-Vary` 响应头做缓存键变体:指令用逗号分隔,值列表用管道符分隔,例如 `Netlify-Vary: query=item_id|page, country=es+de|us, cookie=ab_test|is_logged_in`。
- query 变体写法:`query=a|b` 只取子集,裸 `query` 表示全部参数;key 大小写敏感,参数顺序无关。
- header 变体写法:`header=Device-Type|App-Version`,支持自定义头和大多数标准头。
- language 变体写法:`language=en|es+pt`,其中 `+` 表示分组,按 `Accept-Language` 带质量权重检查。
- country 变体写法:`country=us|es+pt`,基于 GeoIP 的 ISO 3166-1 两字母代码,`+` 表示分组。
- cookie 变体写法:`cookie=ab_test|is_logged_in`,只针对指定 key,不要使用整个 `Cookie` 头。
- 避开不能按其变体的头:`Accept*`、`Cache-Control`、`Connection`、`Content-Length`、`Cookie`、`Host`、`If-*`、`Range`、`Referer`、`Upgrade`、`User-Agent`;语言/cookie/格式改用 `Vary: Accept-Language`、`Vary: Cookie` 或对应的 `Netlify-Vary` 指令。
- 遵守一致性规则:同一 URL 的每次响应必须返回相同的 `Netlify-Vary`,第一个被缓存响应的指令生效、后续的会被忽略;`Netlify-Vary` 与标准 `Vary` 都生效(格式/编码用 `Vary`,也用于把指令传给 Cloudflare 等上游 CDN)。
- 用 `Netlify-Cache-Tag: tag1,tag2,tag3` 给响应打标签以便按标签 purge;`Netlify-Cache-Tag`(Netlify CDN)优先于 `Cache-Tag`(传给下游),代理经过会剥离 `Cache-Tag` 的提供商时两者都设置;约束为大小写不敏感、仅 UTF-8、每个 tag ≤1024 字符、每个响应 ≤500 个 tag。
- 用 `Netlify-Cache-ID`(逗号分隔)让响应退出 atomic-deploy 自动失效,它会自动注册为可 purge 的 cache tag,并有单独的 500 个 ID 上限;退出后需在相关变更(如 `Netlify-Cache-ID` 背后的 redirect/proxy 或函数变更)时主动 purge。
- 在已部署的 function 中调用 `purgeCache` 做按需失效,站点 ID 会自动传入;不传参数表示清空整个站点。
- 按标签 purge 时可指定 deploy/subdomain:`await purgeCache({ tags: [cacheTag], deployAlias: "deploy-preview-11", domain: "early-access.company.com" })`。
- 从 CI、本地脚本或 build 中调用时不能用环境凭证,必须传入 `token`(从环境变量读取的 personal access token,绝不硬编码)和 `siteID`。
- Lambda-compatible functions 使用 legacy 签名 `module.exports.handler = async (event, context) => {…}`,必须传入 `context.clientContext.custom.purge_api_token` 作为 token。
- 在函数外部直接调用 API 时,用 `POST https://api.netlify.com/api/v1/purge`,带上 `Authorization: Bearer <personal_access_token>` 和 `Content-Type: application/json`,body 可含 `site_slug`、`cache_tags`、`deploy_alias`、`domain`。
- purge 参数规则:按站点用 `site_id` 或 `site_slug`;按标签用 `cache_tags` 加站点;省略 `cache_tags` 表示清空整个站点,而传入空的 `cache_tags` 列表什么都不清。
- 标识映射:UI 中(Project configuration > General > Project details)Project ID 等于 `site_id`,Project name 等于 `site_slug`。
- 注意速率限制:每个 tag 或 site 每 5 秒只能 purge 两次,超限返回 `429`。
- 使用全局 `caches` 做程序化读写:`caches.open()` 可在任意作用域调用,但 `match`/`put`/`delete` 只能在请求处理器内调用,在模块/全局作用域调用会抛错。
- 使用 CacheStorage 子集:`caches.match(request)` 从任意缓存返回 `Response` 或 `undefined`;`caches.open(name)` 返回 `Cache`,缓存名越少越有意义以避免分片降低命中率。
- 使用 `Cache` 方法(都需先 `caches.open()`):`cache.match(request)`、`cache.put(request, response)`、`cache.add(request)`/`cache.addAll(requests)`、`cache.delete(request)`;`keys()` 未实现,无法列出缓存内容。
- 注意一致性差异:读写强一致,删除最终一致(已删除条目可能短暂仍可返回)。
- 不要缓存这些内容:206 部分响应、`Vary: *`、非 `GET` 方法;响应需带含 `max-age`/`s-maxage` ≥1 秒且为 `public`(非 `private`/`no-cache`/`no-store`)的 cache-control,状态码为 2xx,否则会存储报错;对无法控制的响应用 `fetchWithCache` 重写头。
- 注意每次调用限制:100 次查找、20 次插入/删除;超限后后续查找返回空、写入/删除变成 no-op;限制在同一请求内的多个 edge functions 间共享,serverless 与 edge functions 之间分开;缓存数据按 region 存储(不复制),在重新部署和 `max-age`/`s-maxage` 过期时自动失效。
- 安装 `@netlify/cache`(`npm install @netlify/cache`)以获得辅助函数、时间常量(`MINUTE`/`HOUR`/`DAY`)和供本地开发使用的 `caches` 导出。
- 在框架自带 dev server 中做本地开发绕过:`caches` 全局不属于 Node.js,此时它是 undefined 会抛错,改为 `import { caches } from "@netlify/cache"`;需要 Netlify CLI 20.0.3+,本地不持久化任何内容(查找返回空,写入/删除不生效)。
- 用 `cacheHeaders(settings)` 生成头对象,支持 `ttl`(→s-maxage)、`swr`、`durable`、`tags`、`overrideDeployRevalidation`(如 `["tag"]` 以退出 atomic-deploy 失效)以及 `vary`(`cookie`/`query`/`country`/`language`/`header`,其中 country 的嵌套数组表示 OR);只需要通用(非 Netlify)头时改用 `cdn-cache-control` npm 模块。
- 用 `fetchWithCache(resource, options?, cacheSettings?)` 作为 `fetch` 的替代:命中缓存就返回,否则抓取、存储并返回;`cacheSettings` 会覆盖冲突的响应头,带 `swr` 时自动处理后台重验证。
- 用 `getCacheStatus(response | headers | headerString)` 获取状态,返回 `{ hit, caches: { durable: { hit, stale, stored, ttl }, edge: { hit, stale } } }`。
- 只在直接调用 `cache.match`/`cache.put`(而非 `fetchWithCache`+`swr`)时用 `needsRevalidation(response)` 判断 Cache API 响应是否处于 SWR 窗口内的过期状态;若是则先返回旧响应,再在 `context.waitUntil` 中重验证并把新副本 `cache.put` 回缓存。
- 需要减少函数调用、改善 miss 延迟时给 serverless 响应加 `durable`:没有本地副本的边缘节点会先查共享 durable cache;它最终一致(同一版本各 region 可能仍调用函数几次)、与站点 functions region 同址,兼容 `Netlify-Vary`、SWR 和按需失效;Next.js 在 Next Runtime 5.5.0+ 会自动使用 durable cache。
- 调试时在部署 URL 上检查 Netlify 为所有响应设置的 `Cache-Status`(RFC 9211),关注以 `"Netlify Edge"` 或 `"Netlify Durable"` 开头的值。
- 按 `Cache-Status` 取值判断状态:`"Netlify Edge"; fwd=miss` 表示未缓存;`"Netlify Edge"; hit` 表示命中缓存;`"Netlify Edge"; hit; fwd=stale` 表示 SWR 期间返回旧内容并重验证;durable miss 存储为 `"Netlify Durable"; fwd=uri-miss; stored=true; ttl=3600`;durable 命中为 `"Netlify Durable"; hit; ttl=1234`。
- `ttl` 为负数表示已过期秒数;每个请求可能命中不同的缓存实例,在没有生产流量或 `durable` 时预计要多次请求才能预热出一次命中,可重复请求预热。
适用边界 · 注意事项
- 要缓存的对象是非 GET 请求(POST/PUT/PATCH/DELETE 等)时:CDN 永不缓存这类请求,应改为在 GET 路由上暴露数据。
- 只在 `netlify dev` 本地验证缓存命中/失效时:本地不模拟 CDN 缓存,必须在 Deploy Preview 或生产 URL 上用 `Cache-Status` 验证。
- 项目不部署在 Netlify,或不想依赖 Netlify 专有缓存机制、`api.netlify.com` purge API 与 personal access token 时。