Performance · 2 min read · 19 October 2026

The cookie-free Supabase client

On a public Next.js site, one cookies() call made every page dynamic, so every visit ran a function. The fix is a client that never asks for cookies, plus caching by page type.

A closed cartoon cookie jar set aside while static page cards fly fast from a cloud to a phone and a laptop
1
cookies() call is enough to make a route dynamic
8
items on my checklist for public Next.js sites on Vercel
0
savings claimed here without a measurement

01 · The trap

Reading cookies tells Next.js the page depends on the request.

The common Supabase server client from @supabase/ssr calls cookies(). On a public site with no login, that makes every page dynamic, so every visit becomes a function invocation. On Vercel, page visits, bots, middleware, ISR cycles, function calls, API routes, link prefetches and uncached assets all count as requests.

02 · The fix

Read public data with a client that never touches cookies.

For read-only queries and the sitemap, the plain @supabase/supabase-js client with the anon key is enough. Writes and anything behind auth keep the cookie-aware server client.

import { createClient } from "@supabase/supabase-js";

// Read-only, no cookies: lets public routes stay static
export const publicDb = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);

03 · Cache by page type

Once routes can be static, decide how stale each one may get.

  • home: revalidate every 3,600 s
  • static pages: 86,400 s
  • listings and detail pages: 3,600 s, with generateStaticParams and dynamicParams = true
  • sitemap: 604,800 s

And keep the middleware matcher away from static assets.

04 · Stop paying for robots and prefetches

Some of the traffic isn't people.

Block aggressive SEO crawlers in robots.ts (Ahrefs, Semrush, MJ12, DotBot, BLEX, Petal, Bytespider), but never Googlebot or Bingbot. A link wrapper with prefetch={false} helps where ten visible links would mean ten requests, but only where the faster navigation isn't worth it. Long cache headers on hashed static files do the rest.

05 · Revalidate on publish

Static doesn't have to mean stale.

A secret-checked POST endpoint revalidates the affected paths, and Supabase webhooks call it when content changes. Editors see their changes without the site going dynamic again.

06 · Measure before you claim

I don't have a clean before-and-after yet, so there is no percentage in this article.

Check the build output to see which routes became static, look for x-vercel-cache: HIT on repeat requests, and compare the Usage dashboard over the same period. A target isn't a result.

Next storyA second brain that updates itself