Cloudflare
缓存

缓存

Next.js 提供了多种方法,通过 缓存 (opens in a new tab) 路由和网络请求来提高应用程序的性能。应用程序将尝试在构建时尽可能多地预渲染和缓存数据,以减少向用户提供响应时所需的工作量。

缓存数据使用重新验证进行更新,可以是定期更新或按需更新:

@opennextjs/cloudflare 缓存支持依赖于 3 个组件:

您还可以启用缓存拦截,以避免调用 NextServer 从而加载与页面相关的 javascript。它可以略微提高缓存路由上 ISR/SSG 路由的冷启动性能。 截至目前,缓存拦截不适用于 PPR,并且默认未启用。

此外,某些组件使用 Cache Api (opens in a new tab) 来提高这些不同组件的性能。 如果您计划使用按需重新验证,您还应该使用 缓存清除组件 以便在页面重新验证时自动清除缓存。

适配器为 open-next.config.ts 中配置的每个组件提供了多种实现。

本指南提供了常见用例的指南,然后详细介绍了所有配置选项。

本页所有内容仅涉及 SSG/ISR 和数据缓存,SSR 路由无需任何缓存配置即可开箱即用。

指南

使用重新验证的小型站点

对于小型站点,您应该使用以下实现:

  • 增量缓存:使用 R2 存储数据
  • 队列:使用由 Durable Objects 支持的队列
  • 标签缓存:D1NextModeTagCache
{
  "name": "<WORKER_NAME>",
  // ...
 
  "services": [
    {
      "binding": "WORKER_SELF_REFERENCE",
      "service": "<WORKER_NAME>",
    },
  ],
 
  // R2 增量缓存
  "r2_buckets": [
    {
      "binding": "NEXT_INC_CACHE_R2_BUCKET",
      "bucket_name": "<BUCKET_NAME>",
    },
  ],
 
  // DO 队列
  "durable_objects": {
    "bindings": [
      {
        "name": "NEXT_CACHE_DO_QUEUE",
        "class_name": "DOQueueHandler",
      },
    ],
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["DOQueueHandler"],
    },
  ],
 
  // D1 标签缓存 (Next 模式)
  // 仅当您使用按需重新验证时才需要此项
  "d1_databases": [
    {
      "binding": "NEXT_TAG_CACHE_D1",
      "database_id": "<DATABASE_ID>",
      "database_name": "<DATABASE_NAME>",
    },
  ],
}

使用重新验证的大型站点

对于大型站点,您应该使用 ShardedDOTagCache,它可以比 D1NextModeTagCache 处理更高的负载:

{
  "name": "<WORKER_NAME>",
  // ...
 
  "services": [
    {
      "binding": "WORKER_SELF_REFERENCE",
      "service": "<WORKER_NAME>",
    },
  ],
 
  // R2 增量缓存
  "r2_buckets": [
    {
      "binding": "NEXT_INC_CACHE_R2_BUCKET",
      "bucket_name": "<BUCKET_NAME>",
    },
  ],
 
  // DO 队列和 DO 分片标签缓存
  "durable_objects": {
    "bindings": [
      {
        "name": "NEXT_CACHE_DO_QUEUE",
        "class_name": "DOQueueHandler",
      },
      // 仅当您使用按需重新验证时才需要此项
      {
        "name": "NEXT_TAG_CACHE_DO_SHARDED",
        "class_name": "DOShardedTagCache",
      },
      {
        "name": "NEXT_CACHE_DO_PURGE",
        "class_name": "BucketCachePurge",
      },
    ],
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": [
        "DOQueueHandler",
        // 仅当您使用按需重新验证时才需要此项
        "DOShardedTagCache",
        "BucketCachePurge",
      ],
    },
  ],
}

SSG 站点

如果您的站点是静态的,您不需要队列或标签缓存。您可以使用基于 Workers 静态资源的只读增量缓存来服务于预渲染的路由。

import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import staticAssetsIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/static-assets-incremental-cache";
 
export default defineCloudflareConfig({
  incrementalCache: staticAssetsIncrementalCache,
  enableCacheInterception: true,
});

预发布

对于预发布环境,当您的站点接收来自单个 IP 的低流量时,您可以将 DO 队列替换为内存队列。

参考

静态资源缓存

Worker 不在静态资源之前运行,因此 next.config.tsheaders 选项不适用于公共文件 (public) 和不可变的构建文件(如 _next/static)。

默认情况下,Cloudflare 静态资源头 (opens in a new tab) 使用 max-age=0must-revalidate,允许浏览器缓存资源但需要进行重新验证请求。这与 Next.js 上 public 文件夹的默认行为相同 (opens in a new tab)

Next.js 还会生成在构建之间不会更改的_不可变_文件。这些文件也将通过静态资源提供。为了匹配 Next.js 中不可变资产的默认缓存行为 (opens in a new tab),避免不必要的重新验证请求,请将以下头添加到 public/_headers (opens in a new tab) 文件:

/_next/static/*
  Cache-Control: public,max-age=31536000,immutable

增量静态再生 (ISR)

增量缓存有 3 种存储选项:

  • R2 对象存储: 一种 具有成本效益 (opens in a new tab) 的 S3 兼容对象存储选项,适用于大量非结构化数据。数据存储在单个区域中,这意味着缓存交互可能较慢 - 这可以通过区域缓存来缓解。
  • Workers KV: 一个 快速 (opens in a new tab) 键值存储,它使用 Cloudflare 的 分层缓存 (opens in a new tab) 来提高缓存命中率。当您向 Workers KV 写入缓存数据时,您写入的是任何 Cloudflare 位置都可以读取的存储。这意味着您的应用程序可以获取数据,将其缓存到 KV 中,然后随后世界各地的请求都可以从此缓存中读取。我们不推荐使用 KV,因为它是最终一致的。
  • Workers 静态资源: 增量缓存的只读存储,从 Workers 静态资源 (opens in a new tab) 提供构建时值。此缓存不支持重新验证。
1. 创建 R2 存储桶
npx wrangler@latest r2 bucket create <YOUR_BUCKET_NAME>
2. 将 R2 存储桶和服务绑定添加到您的 Worker

您的应用程序 worker 中使用的绑定名称是 NEXT_INC_CACHE_R2_BUCKET。服务绑定应该是对您的 worker 的自引用,其中 <WORKER_NAME> 是您的 wrangler 配置文件中的名称。

R2 存储桶使用的前缀可以通过 NEXT_INC_CACHE_R2_PREFIX 环境变量进行配置,默认为 incremental-cache

// wrangler.jsonc
{
  // ...
  "name": "<WORKER_NAME>",
  "r2_buckets": [
    {
      "binding": "NEXT_INC_CACHE_R2_BUCKET",
      "bucket_name": "<BUCKET_NAME>",
    },
  ],
  "services": [
    {
      "binding": "WORKER_SELF_REFERENCE",
      "service": "<WORKER_NAME>",
    },
  ],
}
3. 配置缓存

在您项目的 OpenNext 配置中,启用 R2 缓存。

您可以选择设置一个区域缓存以与 R2 增量缓存一起使用。这将启用更快的缓存条目检索,并减少发送到对象存储的请求量。

区域缓存有两种模式:

  • short-lived:响应最多可重用一分钟。
  • long-lived:获取的响应可重用直到重新验证,ISR/SSG 响应最多可重用 30 分钟。

此外,您可以使用以下选项来自定义区域缓存的行为:

  • shouldLazilyUpdateOnCacheHit:指示缓存惰性更新,意味着当从缓存请求数据时,会向 R2 存储桶发送后台请求以获取最新条目。这对于 long-lived 模式默认启用。
  • bypassTagCacheOnCacheHit:指示缓存在区域缓存命中时不检查标签缓存。这有助于减少响应时间。使用此选项时,您需要确保缓存被正确清除, either by enabling the automatic cache purge or purging the cache manually. 默认为 false
// open-next.config.ts
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache";
// ...
 
// 启用区域缓存:
export default defineCloudflareConfig({
  incrementalCache: withRegionalCache(r2IncrementalCache, {
    mode: "long-lived",
    bypassTagCacheOnCacheHit: true,
  }),
  // ...
});
 
// 不使用区域缓存:
export default defineCloudflareConfig({
  incrementalCache: r2IncrementalCache,
  // ...
});

队列

对于使用基于时间的重新验证的项目,必须设置队列。 当不使用重新验证或仅使用按需重新验证时,不需要它。

配置队列

在您项目的 OpenNext 配置中,启用缓存并设置队列。

Durable Object 队列将在需要时向页面发送重新验证请求,并提供对请求去重的支持。 默认情况下,最多将有 10 个 Durable Object 队列实例,它们每个可以并行处理最多 5 个请求,最多支持 50 个并发 ISR 重新验证。

// open-next.config.ts
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
// ...
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
 
export default defineCloudflareConfig({
  // ...
  incrementalCache: r2IncrementalCache,
  queue: doQueue,
});

您还需要在 wrangler.jsonc 文件中添加一些绑定。

"durable_objects": {
    "bindings": [
      {
        "name": "NEXT_CACHE_DO_QUEUE",
        "class_name": "DOQueueHandler"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["DOQueueHandler"]
    }
  ],

您可以使用环境变量自定义队列的行为:

  • 一个 durable object 实例同时可以处理的最大重新验证数量 (NEXT_CACHE_DO_QUEUE_MAX_REVALIDATION)
  • 重新验证在被视为失败之前可以花费的最大毫秒数 (NEXT_CACHE_DO_QUEUE_REVALIDATION_TIMEOUT_MS)
  • 如果重新验证失败,多长时间后将再次尝试。如果再次失败,它将指数退避直到达到最大重试间隔 (NEXT_CACHE_DO_QUEUE_RETRY_INTERVAL_MS)
  • 可以尝试重新验证路径的最大次数 (NEXT_CACHE_DO_QUEUE_MAX_RETRIES)
  • 为此 durable object 禁用 SQLite。仅当您的增量缓存不是最终一致时才应使用 (NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE)
💡

队列还有 2 种额外模式供您使用:direct 和内存队列

  • 内存队列将去重请求,但仅在每个 isolate 基础上。它不完全适合生产部署,您可以自担风险使用!

  • 队列的 direct 模式旨在用于调试目的,不建议在生产中使用。它仅在预览模式下工作(即 wrangler dev

    对于使用 Page Router 的应用,res.revalidate 需要提供名为 WORKER_SELF_REFERENCE 的自引用服务绑定。

在某些情况下,您可能会遇到 Durable Object 队列可以为单个页面或路由管理的限制。在这种情况下,您可以利用 queueCache 来减少发送到队列的陈旧请求数量。这是通过在将请求分派到队列之前通过 Cache API 添加和验证缓存条目来实现的。如果缓存条目已存在,则请求不会发送到队列,因为它将被视为已在处理中。

// open-next.config.ts
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
// ...
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
import queueCache from "@opennextjs/cloudflare/overrides/queue/queue-cache";
 
export default defineCloudflareConfig({
  // ...
  incrementalCache: r2IncrementalCache,
  queue: queueCache(doQueue, {
    regionalCacheTtlSec: 5, // 区域缓存的 TTL,默认为 5 秒
 
    // 是否在返回之前等待队列确认请求
    // 当设置为 false 时,缓存将尽快填充,队列将在之后调用。
    // 当设置为 true 时,缓存仅在收到队列确认后才填充。
    waitForQueueAck: true,
  }),
});

用于按需重新验证的标签缓存

标签重新验证机制可以使用 Cloudflare D1 (opens in a new tab) 数据库或 Durable Objects (opens in a new tab)SqliteStorage 作为其支持存储,用于存储有关标签、路径和重新验证时间的信息。

要使用按需重新验证,您还应该遵循 ISR 设置步骤

💡

如果您的应用使用 pages router,则不需要标签缓存,应跳过此步骤。 如果您的应用不使用 revalidateTag 也不使用 revalidatePath,您也可以跳过此步骤。

标签缓存有 2 种不同的选项可供选择:d1NextTagCachedoShardedTagCache。 选择哪一个应基于两个关键因素:

  1. 预期负载:考虑您预期的流量或数据量。
  2. revalidateTag / revalidatePath 的使用:评估这些功能的使用频率。

如果这些因素中的任何一个很大,建议使用分片数据库。此外,结合区域缓存可以进一步提高性能。

创建 D1 数据库和服务绑定

您的应用程序 worker 中使用的绑定名称是 NEXT_TAG_CACHE_D1WORKER_SELF_REFERENCE 服务绑定应该是对您的 worker 的自引用,其中 <WORKER_NAME> 是您的 wrangler 配置文件中的名称。

// wrangler.jsonc
{
  // ...
  "d1_databases": [
    {
      "binding": "NEXT_TAG_CACHE_D1",
      "database_id": "<DATABASE_ID>",
      "database_name": "<DATABASE_NAME>",
    },
  ],
  "services": [
    {
      "binding": "WORKER_SELF_REFERENCE",
      "service": "<WORKER_NAME>",
    },
  ],
}

为标签重新验证创建表

D1 标签缓存需要一个 revalidations 表来跟踪按需重新验证时间。

配置缓存

在您项目的 OpenNext 配置中,启用 R2 缓存并设置队列(见上文)。队列将在需要时向页面发送重新验证请求,但它不会对请求去重。

// open-next.config.ts
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache";
 
export default defineCloudflareConfig({
  incrementalCache: r2IncrementalCache,
  queue: doQueue,
  tagCache: d1NextTagCache,
});
4. 在部署期间初始化缓存

为了使缓存正确初始化构建时重新验证数据,您需要作为部署步骤的一部分运行命令。这应作为每次部署的一部分运行,以确保缓存填充了每个构建的数据。

为了同时填充远程绑定并创建应用程序的新 版本 (opens in a new tab),您可以使用 deploy 命令或 upload 命令。同样,preview 命令将填充您的本地绑定并启动 Wrangler 开发服务器。

自动缓存清除

💡

您只能在区域上启用缓存清除功能(例如,使用自定义域名时)。

缓存清除组件在页面重新验证时自动清除缓存。仅当您使用按需重新验证以及利用 Cache API 的缓存组件之一时,才需要它。

此组件可以直接调用 Cache API 的清除函数,或者通过中间 durable object 路由清除请求。使用 durable object 有助于缓冲请求并避免达到 API 速率限制 (opens in a new tab)

仅当您调用 revalidateTagrevalidatePath 或 pages router 中的 res.revalidate 时,才会调用缓存清除。它不适用于 ISR 重新验证。

要使用缓存清除,您需要定义以下 wrangler 密钥:

以下是将缓存清除组件集成到您的 open-next.config.ts 中的示例配置:

import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache";
import doShardedTagCache from "@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache";
import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
import { purgeCache } from "@opennextjs/cloudflare/overrides/cache-purge/index";
 
export default defineCloudflareConfig({
  incrementalCache: withRegionalCache(r2IncrementalCache, { mode: "long-lived" }),
  queue: doQueue,
  // 仅当您使用按需重新验证时才需要此项
  tagCache: doShardedTagCache({ baseShardSize: 12 }),
  // 如果您想使用 PPR,请禁用此项
  enableCacheInterception: true,
  // 您也可以使用 `durableObject` 选项将 durable object 用作缓存清除
  cachePurge: purgeCache({ type: "direct" }),
});

如果您想使用 durable object 选项,您需要在 wrangler.jsonc 文件中添加以下绑定:

{
  "durable_objects": {
    "bindings": [
      {
        "name": "NEXT_CACHE_DO_PURGE",
        "class_name": "BucketCachePurge",
      },
    ],
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["BucketCachePurge"],
    },
  ],
}

您可以使用 NEXT_CACHE_DO_PURGE_BUFFER_TIME_IN_SECONDS 环境变量自定义缓存清除缓冲的持续时间。默认值为 5 秒。它的工作原理是在给定时间内缓冲清除请求,然后一次性发送它们。这有助于避免触及 API 速率限制。

调试

您可以将 NEXT_PRIVATE_DEBUG_CACHE=1 添加到您的应用 .env 文件以调试任何缓存问题。 每当访问缓存时,应用将输出日志 - 这些日志由 Next 和缓存适配器生成。 您可以在 Next 文档 (opens in a new tab) 中找到更多详细信息