Back to Build LogLEARNING

The GSC Indexing Crisis: What await headers() Does to Your Entire Site

await headers() in the root layout forced every page into dynamic rendering. Next.js responded with Cache-Control: private, no-store. Google read private as personalized content and stopped indexing. 100+ pages crawlable, 1 indexed.

D
Diosh Lequiron, PD-SML, PhD, MBA, CSM
April 28, 2026 · 2 min read
#nextjs#seo#indexing#cache-control#app-router#google-search-console

This is the most expensive lesson we learned building on Next.js App Router. We are writing it down so you do not have to learn it the same way.

The Symptom

Google Search Console: 100+ pages crawlable, 1 URL indexed. We had launched with SEO metadata on every public page, a valid sitemap, a minimal robots.txt. Everything looked correct. Nothing was indexing.

The Root Cause

One line in src/app/layout.tsx: const nonce = headers().get(x-nonce). In Next.js App Router, calling any dynamic API (headers(), cookies(), draftMode()) in the root layout forces every downstream page into dynamic rendering mode. Dynamic rendering means Next.js sets: Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate. This overrides anything set in middleware or vercel.json — it is set at the framework level.

Google reads Cache-Control: private as personalized content. It crawled our pages. It read private. It moved on.

Why Middleware Did Not Fix It

We tried setting explicit Cache-Control: public in middleware. It appeared to work in npm start. In Vercel production, Next.js framework-level override won. The framework sets the header after middleware, and it wins. You must verify cache headers on a live deployment URL, not via npm start.

The Fix

Remove await headers() from the root layout. Replace per-request nonce CSP with a static CSP policy. Move any inline scripts to static files in /public/. Rebuild. Pages reclassify from dynamic to static. Cache-Control becomes public, max-age=0, must-revalidate.

The Audit Question This Created

Before shipping any Next.js App Router project: does the root layout call headers(), cookies(), or draftMode()? If yes, every page is dynamic. If the site has public pages that should be indexed, this is a blocking issue.

D

Diosh Lequiron, PD-SML, PhD, MBA, CSM

President & CEO, HavenWizards 88 Ventures

Building arena-forged execution systems and deploying governed Filipino talent across multiple venture lines. Every build log entry comes from real operations, not theory.