Skip to main content

SSR Required for Dynamic Auth Gating on Static Sites

Executive Summary

Auth-gated documentation sites on *.devarno.cloud were permanently redirecting to Casa sign-in, even for authenticated users. The root cause was not the auth middleware, not the cookie configuration, not the BetterAuth setup — it was Starlight’s default prerender: true, which executes middleware at build time and bakes redirect responses into static HTML. A single config change — prerender: false in Starlight’s integration options — switches pages from static prerendering to server-side rendering, ensuring the middleware runs at request time with access to real cookies.

The Decision

When adding auth gating to Starlight-based documentation sites, the middleware approach (Astro defineMiddleware + forwarded cookies to Casa /api/auth/app-session) was sound. The SDK (hub-auth.ts) and the BetterAuth cross-subdomain cookie config (domain: '.devarno.cloud') were both correct. The problem was architectural: prerendered pages execute middleware at build time, not at request time. There are no cookies at build time, so auth always fails, and the redirect response becomes the permanent static output. This affected both Grace (grace.devarno.cloud) and Meridian (meridian.devarno.cloud). Meridian had edgeMiddleware: true but the same fundamental issue — prerendering still bakes middleware responses into static files before any edge function runs.

What Made This Work

1. Diagnostic via Response Headers

The deployed response showed Cache-Control: public, max-age=0, must-revalidate (Vercel static default) instead of the middleware’s private, no-store. Combined with Content-Length: 640 matching the local build artifact exactly, this confirmed the middleware was not executing at request time.

2. Starlight’s Built-in SSR Support

Starlight has a prerender config option that defaults to true. Setting it to false switches the injected route from routes/static/index.astro to routes/ssr/index.astro. This is a supported, documented feature — not a hack or workaround.

3. Vercel Routing Adapts Automatically

With prerender: false, the Astro Vercel adapter generates a catch-all route (^(?:/(.*?))?/?$ to _render) in config.json. No manual Vercel configuration needed.

Key Learning

Static-site generators and request-time middleware are fundamentally incompatible. Middleware that depends on request context (cookies, headers, auth tokens) cannot run during prerendering. This is not a Starlight bug — it’s a category error. The fix is always to switch to SSR for pages that require runtime decisions. The corollary: when adding auth to any static-first framework (Starlight, Nextra, VitePress), always check whether the framework prerenders pages and whether that prerendering executes middleware. If yes, disable prerendering for gated content.

Operational Impact

  • Sites affected: Grace, Meridian (both Starlight + Vercel)
  • Fix complexity: 1 line per site (prerender: false)
  • Trade-off: Pagefind search disabled (Starlight constraint); SSR adds cold-start latency
  • Verification: Check Cache-Control: private, no-store in response headers post-deploy

Session: grace-auth-gating-prerender-fix-2026-04-08 Owner: Dev4rno Archive: devarno-cloud/atlas/learnings/2026-04-08-ssr-required-for-dynamic-auth-gating-on-static-sites.md