Cache Invalidation for AI Consumers: Keeping Agent-Facing Endpoints Fresh Without Busting the CDN Edge A new caching strategy for AI agent-facing endpoints uses short max-age, long stale-while-revalidate, ETags, and tag-keyed purges to keep data fresh without collapsing CDN edge hit ratios. The approach prevents stale data from poisoning multi-step agent reasoning while maintaining low latency for LLM tool calls. The Problem Agent-facing endpoints — the /api/ routes that LLM tool calls, retrieval pipelines, and autonomous agents hit dozens of times per task — sit awkwardly between two cache models. Human-facing HTML can tolerate a 60-second stale window because a person won't notice; an agent reasoning over a chain of five tool calls absolutely will, because stale data in call 2 poisons every downstream inference. The naive fix — Cache-Control: no-store everywhere — collapses your edge hit ratio and pushes every agent request to origin, which is the failure mode CDNs were built to prevent Source 2 source-2 . The Shape js // app/api/agent/ resource /route.ts import { NextRequest, NextResponse } from 'next/server' import { revalidateTag } from 'next/cache' export const dynamic = 'force-dynamic' const FRESH = 30 const SWR = 300 export async function GET req: NextRequest, { params }: { params: { resource: string } } { const tag = agent:${params.resource} const etag = await computeEtag params.resource if req.headers.get 'if-none-match' === etag { return new NextResponse null, { status: 304, headers: { 'Cache-Control': public, max-age=${FRESH}, stale-while-revalidate=${SWR} , 'ETag': etag, 'Vary': 'Accept, X-Agent-Consumer', 'X-Cache-Tag': tag, }, } } const data = await loadResource params.resource, { tag } return NextResponse.json data, { headers: { 'Cache-Control': public, max-age=${FRESH}, stale-while-revalidate=${SWR} , 'ETag': etag, 'Vary': 'Accept, X-Agent-Consumer', 'X-Cache-Tag': tag, 'X-Deployment-Id': process.env.NEXT DEPLOYMENT ID ?? 'dev', }, } } // app/api/invalidate/route.ts export async function POST req: NextRequest { const secret = req.headers.get 'x-invalidate-secret' if secret == process.env.INVALIDATE SECRET { return new NextResponse 'forbidden', { status: 403 } } const { tags } = await req.json as { tags: string } for const t of tags revalidateTag t await fetch 'https://api.cloudflare.com/client/v4/zones/' + process.env.CF ZONE + '/purge cache', { method: 'POST', headers: { 'Authorization': Bearer ${process.env.CF TOKEN} , 'Content-Type': 'application/json', }, body: JSON.stringify { tags } , } return NextResponse.json { purged: tags } } async function computeEtag resource: string : Promise