Next.js · npm

Cache Next.js by tag, in one line.

@canner-ca/next-cache sets the Cache-Control and Surrogate-Key headers Canner needs — correctly, every time — in any Next.js place you can set response headers: Route Handlers, Middleware, and getServerSideProps. Canner is App Router aware, so content pages cache for cold loads and crawlers while client navigations always hit your app. Zero dependencies.

$npm install @canner-ca/next-cache

Use it

Route Handlers

// app/api/posts/route.ts
import { NextResponse } from 'next/server';
import { cache } from '@canner-ca/next-cache';

export async function GET() {
  const res = NextResponse.json(await getPosts());
  cache(res, { ttl: 3600, tags: ['blog-listing'] });
  return res;
}

Middleware (App Router pages)

Server components can't set response headers, so you cache App Router pages from middleware. Tag by a path-derived key your CMS webhook can purge.

// middleware.ts
import { NextResponse } from 'next/server';
import { cache } from '@canner-ca/next-cache';

export function middleware(request) {
  const res = NextResponse.next();
  if (request.nextUrl.pathname.startsWith('/blog/')) {
    const slug = request.nextUrl.pathname.split('/')[2];
    cache(res, { ttl: 3600, tags: [slug, 'blog-listing'] });
  }
  return res;
}

Pages Router

In getServerSideProps or an API route, pass the Node response object.

export async function getServerSideProps({ res, params }) {
  const post = await getPost(params.slug);
  cache(res, { ttl: 600, tags: [post.id] });
  return { props: { post } };
}

How App Router caching works

A page URL serves two things: the HTML document (a cold load, a crawler, a hard navigation — no RSC header) and an RSC payload (client navigation/prefetch — sends an RSC header). Canner caches only the HTML document and passes every RSC request straight through to your app. Marking a page cacheable is therefore safe: client-side navigation is never stale or mismatched, and the cache speeds up exactly the requests that matter for SEO and first paint. You don't configure any of this — it's how Canner treats Next.js.

Options

ttlRequired. Seconds Canner may serve the cached response — sets s-maxage.tagsString, number, or array. Sets Surrogate-Key. Numbers are coerced; duplicates and whitespace tags are dropped.browserTtlOptional. Seconds the visitor's browser may cache (sets max-age). Omit to keep tag purges instant.

It only ever adds headers

The helper never strips or mutates anything your app set. It does not remove Set-Cookie — Canner already declines to cache a response that sets a cookie, so you get a development-only warning, and your cookie is left untouched. A bad TTL sets no headers and warns; only passing something that isn't a response or Headers throws.

Then point your CMS at Canner

The code side is done. The other half is two copy-paste values — the webhook URL and an Authorization header — shown pre-filled in the dashboard under Settings → Caching. Full caching guide.

Source and issues: tools/next-cache/ in the canner repo on GitHub. Licensed MIT. For Astro, see @canner-ca/astro-cache.