This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
frontendcritical PriorityID: nextjs15-ppr-dynamic-hole-cache-poisoning-guardrail

Next.js 15 PPR Dynamic Hole Cache Poisoning Guardrail

Enforces strict boundary isolation between static shell generation and dynamic streaming holes in Next.js 15 Partial Prerendering (PPR). Prevents accidental evaluation and exposure of session identifiers, authorization tokens, or personalized tenant data inside statically prerendered shells and shared CDN edge caches.

Prohibited Anti-Pattern: Leaking Personalized Tenant Context or Session Tokens into Prerendered Shell Fallbacks under Next.js 15 PPR

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Next.js 15 PPR Dynamic Hole Cache Poisoning Guardrail - Enforces strict boundary isolation between static shell generation and dynamic streaming holes in Next.js 15 Partial Prerendering (PPR). Prevents accidental evaluation and exposure of session identifiers, authorization tokens, or personalized tenant data inside statically prerendered shells and shared CDN edge caches.
globs: app/**/*.{ts,tsx}, src/app/**/*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/nextjs15-ppr-dynamic-hole-cache-poisoning-guardrail"
---

# Next.js 15 PPR Dynamic Hole Cache Poisoning Guardrail

> **Severity**: CRITICAL | **Category**: frontend
> **Anti-Pattern Warning**: Leaking Personalized Tenant Context or Session Tokens into Prerendered Shell Fallbacks under Next.js 15 PPR

## System Constraints
You are a Principal Software Architect enforcing runtime and compile-time boundaries for Next.js 15 Partial Prerendering (PPR). Next.js 15 compiles the outer route tree and Suspense fallbacks into an immutable static shell distributed across public CDN edges. Dynamic holes stream user-specific data only within isolated Suspense boundaries. You must reject any code pattern where authentication state, cookies, headers, personalized tenant identifiers, or private cache keys bleed into the prerendered shell or fallback tree.

## Strict Guidelines
- Engineers MUST ALWAYS isolate all dynamic, identity-bearing Server Components within React `<Suspense>` boundaries when `experimental_ppr = true` is enabled.
- Engineers MUST NOT read dynamic data sources (`cookies()`, `headers()`, search params, or auth tokens) in `<Suspense>` fallback trees or outside dynamic child boundaries.
- Suspense fallbacks MUST remain entirely identity-agnostic, deterministic, and free of personalized layout logic to prevent edge cache poisoning of tenant data.
- Components accessing dynamic context in Next.js 15 SHOULD call `await connection()` from `next/server` before executing non-cacheable authorization or session validation logic.
- Engineers MUST NEVER mark layouts as dynamic merely to bypass PPR; instead, use granular Suspense dynamic holes around specific personalized widgets.

## Bad vs Good Implementations
### Secure Dynamic Suspense Hole vs Prerendered Fallback Cache Poisoning

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Accessing `cookies()` at the page root level outside a Suspense boundary or passing dynamic tenant details into the `<Suspense fallback>` pollutes the static prerender phase. In Next.js 15 PPR, the fallback tree is compiled into the static shell cached at the edge CDN, exposing tenant credentials to unauthenticated users or poisoning the shared cache.
```typescript
// app/dashboard/page.tsx
import { cookies } from 'next/headers';
import { Suspense } from 'react';

export const experimental_ppr = true;

// ANTI-PATTERN: The fallback receives session context, or dynamic headers
// are executed outside of a Suspense dynamic leaf, baking confidential data
// into the static CDN shell or causing build-time credential leakage.
export default async function DashboardPage() {
  const cookieStore = await cookies();
  const tenantId = cookieStore.get('x-tenant-id')?.value;

  return (
    <main className="dashboard-container">
      <header>
        <h1>Organization Dashboard</h1>
        {/* CRITICAL: Fallback includes tenant-specific data baked into the static shell */}
        <Suspense fallback={<p>Loading metrics for {tenantId}...</p>}>
          <MetricsWidget tenantId={tenantId} />
        </Suspense>
      </header>
    </main>
  );
}

async function MetricsWidget({ tenantId }: { tenantId?: string }) {
  const data = await fetch(`https://api.internal/metrics?tenant=${tenantId}`);
  const metrics = await data.json();
  return <div>Active Users: {metrics.active}</div>;
}
```

#### Production Standard (Correct)
> **Standard**: The page entry point remains static and safe for global edge CDN distribution. Dynamic state (`cookies()`, `connection()`, and private API calls) is quarantined inside `<DynamicMetricsHole />`. The Suspense fallback (`<MetricsSkeleton />`) contains no user/tenant state, preventing cache poisoning across multi-tenant boundaries.
```typescript
// app/dashboard/page.tsx
import { connection } from 'next/server';
import { cookies } from 'next/headers';
import { Suspense } from 'react';

export const experimental_ppr = true;

// PRODUCTION FIX: The page shell is completely static and deterministic.
// Personalized reads are deferred to the streaming hole inside <Suspense>.
export default function DashboardPage() {
  return (
    <main className="dashboard-container">
      <header>
        <h1>Organization Dashboard</h1>
      </header>
      {/* Static shell renders a generic, identity-blind skeleton */}
      <Suspense fallback={<MetricsSkeleton />}>
        <DynamicMetricsHole />
      </Suspense>
    </main>
  );
}

function MetricsSkeleton() {
  return <div className="skeleton animate-pulse h-12 w-48 bg-gray-200" />;
}

async function DynamicMetricsHole() {
  // Explicit dynamic gate prevents prerender execution
  await connection();
  const cookieStore = await cookies();
  const tenantId = cookieStore.get('x-tenant-id')?.value;

  if (!tenantId) {
    throw new Error('Unauthorized dynamic hole access');
  }

  const response = await fetch(`https://api.internal/metrics?tenant=${tenantId}`, {
    cache: 'no-store',
  });
  const metrics = await response.json();

  return <div>Active Users: {metrics.active}</div>;
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Next.js 15 PPR Dynamic Hole Cache Poisoning Guardrail

Next.js 15 PPR Dynamic Hole Cache Poisoning Guardrail defines an industrial-grade behavioral standard for frontend engineering. Enforces strict boundary isolation between static shell generation and dynamic streaming holes in Next.js 15 Partial Prerendering (PPR). Prevents accidental evaluation and exposure of session identifiers, authorization tokens, or personalized tenant data inside statically prerendered shells and shared CDN edge caches. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Leaking Personalized Tenant Context or Session Tokens into Prerendered Shell Fallbacks under Next.js 15 PPR Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftLeaking Personalized Tenant Context or Session Tokens into Prerendered Shell Fallbacks under Next.js 15 PPRCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS isolate all dynamic, identity-bearing Server Components within React `<Suspense>` boundaries when `experimental_ppr = true` is enabled.

Engineers MUST ALWAYS isolate all dynamic, identity-bearing Server Components within React `<Suspense>` boundaries when `experimental_ppr = true` is enabled.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST NOT read dynamic data sources (`cookies()`, `headers()`, search params, or auth tokens) in `<Suspense>` fallback trees or outside dynamic child boundaries.

Engineers MUST NOT read dynamic data sources (`cookies()`, `headers()`, search params, or auth tokens) in `<Suspense>` fallback trees or outside dynamic child boundaries.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Suspense fallbacks MUST remain entirely identity-agnostic, deterministic, and free of personalized layout logic to prevent edge cache poisoning of tenant data.

Suspense fallbacks MUST remain entirely identity-agnostic, deterministic, and free of personalized layout logic to prevent edge cache poisoning of tenant data.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Components accessing dynamic context in Next.js 15 SHOULD call `await connection()` from `next/server` before executing non-cacheable authorization or session validation logic.

Components accessing dynamic context in Next.js 15 SHOULD call `await connection()` from `next/server` before executing non-cacheable authorization or session validation logic.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Engineers MUST NEVER mark layouts as dynamic merely to bypass PPR

Engineers MUST NEVER mark layouts as dynamic merely to bypass PPR; instead, use granular Suspense dynamic holes around specific personalized widgets.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Next.js 15 PPR Dynamic Hole Cache Poisoning Guardrail critical for AI coding assistants?

Enforces strict boundary isolation between static shell generation and dynamic streaming holes in Next.js 15 Partial Prerendering (PPR). Prevents accidental evaluation and exposure of session identifiers, authorization tokens, or personalized tenant data inside statically prerendered shells and shared CDN edge caches. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Leaking Personalized Tenant Context or Session Tokens into Prerendered Shell Fallbacks under Next.js 15 PPR) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related FRONTEND Rules

View all rules →
frontend
119

SolidStart 1.0 Server Functions & Async Boundaries Production Standard

SolidStart 1.0 relies on the Vinxi bundler to split `server$` closures into independent HTTP POST RPC endpoints. Direct lexical closure over client signals within `server$` scopes or failure to demarcate async boundaries with `<Suspense>` causes silent serialization failures, client-bundle secret leaks, and SSR hydration mismatches. Enforcing strict parameter passing, runtime input validation, and Solid async primitives maintains security and fine-grained reactivity across network boundaries.

frontend
119

Astro 5 Actions & Content Layer Production Standard

Enforces strict type safety and architectural correctness for Astro 5 Server Actions and the zero-waterfall Content Layer API. Prevents runtime contract mismatches, hydration mismatches, and unhandled mutation exceptions across dynamic and prerendered boundaries. Failure to follow these patterns leads to silent data fetching waterfalls, build-time cache corruptions, and unhandled server action exceptions.

frontend
51

Nuxt 4 Nitro Server Engine & Shared Directory Production Standard

Nuxt 4 enforces strict boundary segregation between client UI in the app/ directory, backend endpoints in server/, and universal contracts in shared/. Violating cross-boundary isolation or maintaining module-scoped state in Nitro server handlers causes catastrophic cross-request data corruption and hydration mismatches under concurrent production traffic. Architects must enforce request-isolated H3 event contexts, universal serialization schemas, and strict path-alias boundaries.

1 Rule in Stack

Next.js