GET routes that return the same data on every request — product listings, published posts, public user profiles — without any caching strategy convert every user visit into a full database round-trip. At modest scale this is waste; at traffic spikes it is the mechanism by which a popular moment becomes a database overload event. Even a 60-second stale-while-revalidate cache cuts database load by roughly 100x for a route that serves one request per second, while keeping data within a minute of fresh. OWASP LLM10 (Unbounded Consumption) identifies uncached expensive operations as a key cost amplification vector — the same logic applies to database-backed GET routes that serve identical responses to many users.
Info because missing caching is an efficiency gap rather than a security or correctness defect — the application functions correctly, it just costs more to operate than necessary.
Add Cache-Control: public, s-maxage=60, stale-while-revalidate=300 to read-only GET routes that serve public data. In a Next.js Route Handler:
export async function GET() {
const posts = await prisma.post.findMany({
where: { published: true },
orderBy: { publishedAt: 'desc' },
take: 20,
})
return Response.json(posts, {
headers: {
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
},
})
}
Alternatively, export revalidate = 60 at the route segment level to use Next.js's built-in ISR. Reserve cache: 'no-store' for routes that return user-specific or time-critical data. Document the caching intent with a comment so future developers know the omission is intentional when it is.
ID: ai-slop-cost-bombs.cache-idempotency.stale-while-revalidate-considered
Severity: info
What to look for: Walk all API handler files exporting GET. Count all GET handlers and verify whether each one sets cache headers (cache-control, s-maxage, stale-while-revalidate), uses unstable_cache, exports revalidate, or uses Next.js fetch caching. EXCLUDE handlers that legitimately need to be uncached (auth checks, real-time data).
Pass criteria: At least 30% of GET handlers consider caching strategy (cache headers, revalidate, etc.). Report: "X GET handlers inspected, Y with caching strategy, Z uncached."
Fail criteria: Fewer than 30% of GET handlers have any caching strategy AND the project has more than 5 GET handlers.
Skip (N/A) when: Project has fewer than 5 GET handlers.
Detail on fail: "8 GET handlers, 1 with caching strategy. The other 7 have no Cache-Control headers and no Next.js revalidate — every request is a database hit"
Remediation: Even a 60-second cache cuts database load by 100x for popular routes. Add cache headers to read-only GET routes:
export async function GET() {
const data = await prisma.post.findMany({ where: { published: true } })
return Response.json(data, {
headers: {
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
},
})
}