Skip to content

fix: courses.opengolfapi.org rebuilds every page on every request (4s TTFB) - #2

Open
Geekur wants to merge 1 commit into
mainfrom
fix/opennext-incremental-cache
Open

fix: courses.opengolfapi.org rebuilds every page on every request (4s TTFB)#2
Geekur wants to merge 1 commit into
mainfrom
fix/opennext-incremental-cache

Conversation

@Geekur

@Geekur Geekur commented Aug 10, 2026

Copy link
Copy Markdown
Member

The symptom

courses.opengolfapi.org takes ~4 seconds to return its first byte. The network is not the problem — from a host 22ms away, DNS+connect+TLS totals 0.11s and robots.txt on the same origin returns in 0.17s.

Six consecutive requests:

# TTFB cache
1 4.06s MISS
2 3.89s MISS
3 4.02s MISS
4 3.87s MISS
5 3.81s MISS
6 3.93s MISS

Consistent. Not a cold start.

The cause

The response already says it is cacheable and prerendered:

x-nextjs-prerender: 1
cache-control: s-maxage=3600, stale-while-revalidate=31532400
x-nextjs-cache: MISS

But open-next.config.ts was:

export default defineCloudflareConfig({});

No incrementalCache, and wrangler.toml bound no R2 bucket and no KV namespace. OpenNext had nowhere to persist a rendered page, so every prerendered route was regenerated from Supabase on every request. The page was built and then thrown away, forever.

The fix

import r2IncrementalCache from '@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache';
export default defineCloudflareConfig({ incrementalCache: r2IncrementalCache });

plus the binding:

[[r2_buckets]]
binding = "NEXT_INC_CACHE_R2_BUCKET"
bucket_name = "opengolfapi-next-cache"

Bucket opengolfapi-next-cache has been created.

Verified, not assumed

Against the installed @opennextjs/cloudflare@1.19.8:

  • the package exports map routes ./*./dist/api/*.js, so overrides/incremental-cache/r2-incremental-cache resolves to a file that exists
  • r2-incremental-cache.js reads exactly NEXT_INC_CACHE_R2_BUCKET — the binding name is not arbitrary

This is not unique to golf

The same empty defineCloudflareConfig({}) appears in monorepo/opengolfid-web and monorepo/web. Both are likely paying the same cost and should be checked.

Meanwhile the account already has altar-website-opennext-cache and xtrends-web-cache — this pattern is already running correctly elsewhere. Courses is the app that got missed.

Expected result

First request per route still pays the render. Every subsequent request inside the hour serves from R2 — milliseconds, not seconds.

…e, every request

open-next.config.ts was defineCloudflareConfig({}) with no incrementalCache,
and wrangler.toml bound no R2 bucket or KV namespace. OpenNext therefore had
no store to write rendered pages into, so every prerendered route was rebuilt
from Supabase on every single request.

Measured on courses.opengolfapi.org, 2026-08-10, six consecutive requests:

  TTFB 4.06s / 3.89s / 4.02s / 3.87s / 3.81s / 3.93s
  x-nextjs-cache: MISS   (every time)
  x-nextjs-prerender: 1
  cache-control: s-maxage=3600, stale-while-revalidate=31532400
  robots.txt on the same host: 0.17s

Consistent, not a cold start. The page was prerendered and declared an hour of
cacheability; there was nowhere to put it.

Bucket opengolfapi-next-cache was created for this. The account already runs
this pattern elsewhere (altar-website-opennext-cache, xtrends-web-cache) — the
courses app is the one that never got wired up.

Verified against the installed @opennextjs/cloudflare 1.19.8, not from memory:
the exports map routes ./* to dist/api/*, so the override path resolves, and
r2-incremental-cache.js reads the binding name NEXT_INC_CACHE_R2_BUCKET.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant