docs(cache): set Vercel edge Cache-Control for HTML pages (RFC#562 step 1) #56

Merged
core-devops merged 1 commits from docs/rfc562-cache-headers into main 2026-05-19 19:15:49 +00:00
Member

Summary

Fixes the HTML cache-header gap on docs.moleculesai.app called out in internal#562 audit. Currently HTML pages return Cache-Control: max-age=0, must-revalidate even though x-vercel-cache: HIT — every nav still does a round-trip just for a 304.

Adds a single headers() rule in next.config.mjs:

  • All paths except /_next/static, /_next/image, /api/*: public, max-age=0, s-maxage=300, stale-while-revalidate=86400 — browser revalidates each nav, edge caches 5 min fresh + 24 h SWR.
  • /_next/static/*: untouched. Next.js sets public, max-age=31536000, immutable and that header CANNOT be overridden in next.config.js per Next.js docs.
  • /_next/image: untouched (image optimizer applies its own cache headers).
  • /api/*: untouched (app-controlled per route).

This repo has no public/ directory so there are no unhashed brand assets to configure separately.

Vendor-doc check

  • next.config.js headers shape: https://nextjs.org/docs/app/api-reference/config/next-config-js/headers{source, headers: [{key, value}]} confirmed.
  • Negative-lookahead pattern /((?!_next/static|_next/image|api/).*) is the canonical Next.js form (see proxy negative-matching example in same docs).
  • Vercel Edge honors s-maxage + stale-while-revalidate semantics for cache TTL and background revalidation.

Expected impact

  • Edge HIT ratio on HTML pages: ~0% to >90% within a 5 min nav burst, >99% for repeat visits within 24 h.
  • Hashed _next/static/* retains immutable (no change).

Out of scope (separate PRs)

  • Cloudflare cache rules on *.moleculesai.app.
  • www.moleculesai.app: shipped in molecule-ai/landingpage PR#13 (same RFC).

Test plan

  • After merge + deploy: curl -sI https://docs.moleculesai.app/ | grep -i cache-control shows s-maxage=300, stale-while-revalidate=86400.
  • curl -sI https://docs.moleculesai.app/docs/google-adk | grep -i cache-control same.
  • curl -sI https://docs.moleculesai.app/_next/static/<chunk>.js | grep -i cache-control still immutable, max-age=31536000.
  • Repeat-curl any HTML to confirm x-vercel-cache: HIT after first request.
  • next build succeeds locally with new config (CI on Vercel will validate).

RFC: internal#562 (step 1)

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

## Summary Fixes the HTML cache-header gap on `docs.moleculesai.app` called out in internal#562 audit. Currently HTML pages return `Cache-Control: max-age=0, must-revalidate` even though `x-vercel-cache: HIT` — every nav still does a round-trip just for a 304. Adds a single `headers()` rule in `next.config.mjs`: - All paths except `/_next/static`, `/_next/image`, `/api/*`: `public, max-age=0, s-maxage=300, stale-while-revalidate=86400` — browser revalidates each nav, edge caches 5 min fresh + 24 h SWR. - `/_next/static/*`: untouched. Next.js sets `public, max-age=31536000, immutable` and that header CANNOT be overridden in `next.config.js` per Next.js docs. - `/_next/image`: untouched (image optimizer applies its own cache headers). - `/api/*`: untouched (app-controlled per route). This repo has no `public/` directory so there are no unhashed brand assets to configure separately. ## Vendor-doc check - next.config.js headers shape: https://nextjs.org/docs/app/api-reference/config/next-config-js/headers — `{source, headers: [{key, value}]}` confirmed. - Negative-lookahead pattern `/((?!_next/static|_next/image|api/).*)` is the canonical Next.js form (see proxy negative-matching example in same docs). - Vercel Edge honors `s-maxage` + `stale-while-revalidate` semantics for cache TTL and background revalidation. ## Expected impact - Edge HIT ratio on HTML pages: ~0% to >90% within a 5 min nav burst, >99% for repeat visits within 24 h. - Hashed `_next/static/*` retains `immutable` (no change). ## Out of scope (separate PRs) - Cloudflare cache rules on `*.moleculesai.app`. - www.moleculesai.app: shipped in molecule-ai/landingpage PR#13 (same RFC). ## Test plan - After merge + deploy: `curl -sI https://docs.moleculesai.app/ | grep -i cache-control` shows `s-maxage=300, stale-while-revalidate=86400`. - `curl -sI https://docs.moleculesai.app/docs/google-adk | grep -i cache-control` same. - `curl -sI https://docs.moleculesai.app/_next/static/<chunk>.js | grep -i cache-control` still `immutable, max-age=31536000`. - Repeat-curl any HTML to confirm `x-vercel-cache: HIT` after first request. - `next build` succeeds locally with new config (CI on Vercel will validate). RFC: internal#562 (step 1) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
documentation-specialist added 1 commit 2026-05-19 19:05:53 +00:00
docs(cache): set Vercel edge Cache-Control for HTML pages (RFC#562 step 1)
Secret scan / secret-scan (pull_request) Successful in 0s
CI / build (pull_request) Successful in 56s
4f8d0f44fb
Per internal#562 audit, HTML pages on docs.moleculesai.app respond with
`Cache-Control: max-age=0, must-revalidate` while Vercel Edge is HITting them.
That means a browser round-trip to the edge on every navigation just to get a
304 — no benefit from edge cache being warm.

Adds a single `headers()` rule in next.config.mjs that sets
`public, max-age=0, s-maxage=300, stale-while-revalidate=86400` on every path
EXCEPT Next.js internals (/_next/static and /_next/image — already immutable
and uncacheable-override per Next.js docs) and /api/* (app-controlled cache).

The source pattern `/((?!_next/static|_next/image|api/).*)` uses path-to-regexp
negative lookahead — same pattern Next.js's own proxy.js doc recommends for
negative matching.

This site has no /public/ dir so there are no unhashed brand assets to
configure separately — those will inherit the same HTML cache rule, which is
the right default for our content (changelog/docs MDX, not high-churn).

Expected impact: edge-HIT ratio on HTML pages rises from ~0% to >90% during
typical nav bursts (5 min freshness) and 99%+ for repeat visits within 24 h
(stale-while-revalidate window). Hashed _next/static assets retain their
`immutable, max-age=31536000` headers — Next.js sets these and they cannot
be overridden in next.config.

RFC: internal#562 (step 1 — Vercel side; CF cache rules tracked separately)
core-security approved these changes 2026-05-19 19:12:19 +00:00
core-security left a comment
Member

docs#56 next.config.mjs Cache-Control addition for HTML pages only. Negative lookahead excludes _next internals + /api/ routes (auth-bearing). No secrets/auth-state cached. Security APPROVE.

docs#56 next.config.mjs Cache-Control addition for HTML pages only. Negative lookahead excludes _next internals + /api/ routes (auth-bearing). No secrets/auth-state cached. Security APPROVE.
core-devops approved these changes 2026-05-19 19:12:20 +00:00
core-devops left a comment
Member

docs#56 RFC#562 step 1: Next.js next.config.mjs headers() async fn — HTML pages s-maxage=300 SWR=86400, _next/static + _next/image + /api/ excluded via negative lookahead (preserves immutable hash-named caching). Clean diff. core-devops APPROVE.

docs#56 RFC#562 step 1: Next.js next.config.mjs headers() async fn — HTML pages s-maxage=300 SWR=86400, _next/static + _next/image + /api/ excluded via negative lookahead (preserves immutable hash-named caching). Clean diff. core-devops APPROVE.
core-devops merged commit aef224fc85 into main 2026-05-19 19:15:49 +00:00
Sign in to join this conversation.
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/docs#56