
Content creator and developer at UICraft Marketplace, sharing insights and tutorials on modern web development.
Save hours of development time with our premium Next.js templates. Built with Next.js 16, React 19, and Tailwind CSS 4.
Get the latest articles, tutorials, and product updates delivered to your inbox.
When your Next.js build fails with "TypeError: Cannot read properties of null (reading 'auth')" on the \_not-found page, it's usually because your layout tries to access authentication context during static prerendering. Here's how to fix it properly.

Next.js Cache Components introduce a powerful static shell optimization, but they conflict with draftMode preview workflows. Learn how to conditionally bypass caching when editors need to preview unpublished content from your headless CMS.

Running `next lint` with ESLint 9 throws cryptic "Unknown options" errors for useEslintrc and extensions. This guide explains why ESLint 9's flat config breaks Next.js linting and provides clear solutions to get your project working again.
If you're running a high-traffic Next.js application—think e-commerce with hundreds of thousands of SKUs, content platforms with dynamic category pages, or any site where CDN cache hit rates directly impact your infrastructure costs—you've likely encountered a frustrating reality: your Next.js RSC CDN efficiency is nowhere near what it should be.
Client-side navigations that should be lightning-fast cache hits are instead round-tripping to your origin server. Your CDN is storing duplicate data under different cache keys. And during traffic spikes, your server is getting hammered when it shouldn't need to be touched at all.
This isn't a misconfiguration on your part. It's a fundamental characteristic of how React Server Component (RSC) payloads interact with CDN caching—and it's been a known pain point that the Next.js team has been actively addressing. Let's dig into what's actually happening, why, and how to fix it.
Consider a typical e-commerce scenario. You have product listing pages (PLPs) for various categories:
/mens/
/mens/trainers
/mens/trainers/brand
/mens/trainers/brand?facet-price=:168
These pages might render many of the same products. A product with ID 299336 could appear on all four pages. Logically, when a user navigates between these category pages, the RSC payload for that product component should be cacheable and reusable.
Here's what you'd expect:
# All these requests should hit the same CDN cache
product/299336 → CDN HIT
product/299336 → CDN HIT
product/299336Here's what actually happens:
# Each navigation creates a unique cache key
product/299336/?_rsc=1vl30 → CDN MISS → origin
product/299336/?_rsc=qe3go → CDN MISS → origin
product/299336/?_rsc=1vg99 → CDN MISS → origin
product/299336/?_rsc=1stsw → CDN MISS → originThe _rsc query parameter changes based on the navigation context, even when the returned data is byte-for-byte identical. This was documented extensively in GitHub Issue #65335, where developers running high-load e-commerce sites reported that effectively caching RSC payloads at the edge was nearly impossible.
The practical impact is severe:
The RSC payload mechanism in Next.js includes a hash that encodes information about the navigation context, not just the target route. Specifically, the _rsc parameter and the Next-Router-State-Tree header encode:
Next-Url headerThis is by design. The RSC protocol needs to know what's already rendered on the client to compute the minimal diff. The problem is that this context-dependent hashing creates cache keys that are too specific for effective CDN caching.
// Imagine navigating to the same product from different pages
// Navigation from /mens/
GET /product/299336?_rsc=abc123
Headers: {
"Next-Router-State-Tree": "[encoded-tree-from-mens]",
"Next-Url": "/mens/"
}
// Navigation from /mens/trainers
GET /product/299336?_rsc=def456
Headers: {
"Next-Router-State-Tree": "[encoded-tree-from-trainers]",
"Next-Url": "/mens/trainers"
}The Vary header returned by Next.js makes this explicit:
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-UrlThis tells the CDN: "The response varies based on all of these headers." Practically, this makes most CDN caching configurations ineffective for RSC payloads.
Starting with specific canary releases (and now stabilized in production versions), the Next.js team addressed the most impactful parts of this issue:
The first step is ensuring you're on a version that includes these optimizations. At time of writing, you should be on at least Next.js 14.3.x for stable fixes, or the latest canary for bleeding-edge improvements.
# For stable (minimum 14.3.x, latest recommended)
npm install next@latest
# For canary with latest optimizations
npm install next@canaryThis is critical. The behavior differs significantly between next dev and next start:
# Build for production
next build
# Run production server
next startOpen Network devtools and navigate between category pages. You're looking for consistency in the _rsc parameter for identical routes:
// After upgrading, navigating to the same product produces consistent keys
// Navigation from /mens/
GET /product/299336?_rsc=stable123
Cache-Control: public, max-age=3600
// Navigation from /mens/trainers (same product, same RSC data)
GET /product/299336?_rsc=stable123 // SAME KEY!
// CDN HIT - served from edgeCreate a simple test script to verify cache behavior:
// scripts/verify-rsc-caching.ts
import { chromium } from "playwright";
async function verifyRSCCaching() {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
const rscRequests: Map<string, string[]> = new Map();
// Intercept RSC requests
page.on("request", (request) => {
const
Run this against your production build:
npx tsx scripts/verify-rsc-caching.tsExpected output for a properly optimized build:
=== RSC Cache Key Analysis ===
✅ OPTIMAL /product/299336
Unique _rsc values: 1
Keys: a3b2c1
If you're still seeing multiple unique _rsc values for the same route, you're likely on an older version or there's something in your route structure causing unnecessary variation.
Preventing regression is as important as fixing the issue. Here's how to build monitoring into your pipeline.
// playwright.config.ts - Add RSC efficiency check
import { defineConfig } from "@playwright/test";
export default defineConfig({
projects: [
{
name: "rsc-efficiency",
testDir: "./tests/performance",
use: {
baseURL: "http://localhost:3000",
},
},
],
});// tests/performance/rsc-cache-efficiency.spec.ts
import { test, expect } from "@playwright/test";
test("RSC payloads should have consistent cache keys", async ({ page }) => {
const rscKeys = new Set<string>();
page.on("request", (req) => {
if (req.url().includes("_rsc=")) {
rscKeys.add(new URL(req.url()).searchParams.get("_rsc"
If you're using Cloudflare, configure analytics to track cache status:
// middleware.ts - Add cache debugging headers
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// Add timing header for cache analysis
response.headers.set("X-Request-Start", Date.now().toString());
// If this is an RSC request, log it for analysis
if (request.headers.get("RSC") === "1") {
Track the ratio of unique RSC keys to unique routes. If this ratio starts climbing, something has regressed:
// lib/metrics.ts
export function trackRSCEfficiency(metrics: {
uniqueRoutes: number;
uniqueRSCKeys: number;
cacheHitRate: number;
}) {
const efficiency = metrics.uniqueRoutes / metrics.uniqueRSCKeys;
// Alert if efficiency drops below threshold
if (efficiency < 0.8) {
console.warn(
`RSC cache efficiency degraded: ${efficiency.toFixed(2)}. ` +
`Expected close to 1.0. Check for Next.js version regression.`
);
// Send to your monitoring service
Even with the Next.js fixes, your CDN configuration matters. Here's what to tune:
// cloudflare-worker.ts
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
// For RSC requests, normalize cache key by stripping context-dependent params
if (request.headers.get("RSC") === "1") {
// Create normalized cache key based only on the path
const cacheKey = new Request(url.pathname, {
method: "GET",
headers: {
RSC: "1",
// Omit Next-Router-State-Tree and Next-Url for cache key
next start — development mode doesn't reflect production caching behaviorThe Next.js team continues to improve RSC caching efficiency with each release. Keep an eye on the GitHub issue for updates, and upgrade regularly to benefit from the latest optimizations.
For high-traffic applications, these optimizations can mean the difference between origin servers crumbling under load versus smoothly serving traffic from edge locations worldwide. The fix is straightforward—the key is knowing to look for it in the first place. ---CONTENT_END---