Route Handlers integrate with Next.js caching through segment config options: use 'force-static' to cache GET responses at build time (like SSG) and 'force-dynamic' for request-time execution (like SSR), with granular controls via revalidate and fetch options
Route Handlers in Next.js have a sophisticated relationship with the framework's caching layers. Unlike Pages Router API routes, which were always dynamic by default, Route Handlers follow a more nuanced model: they can be statically generated at build time, dynamically executed on every request, or cached with ISR-style revalidation. The default behavior changed in Next.js 15—GET handlers are now dynamic by default [citation:4], but you have explicit control through route segment configuration options that determine how and when your handlers execute and cache responses [citation:1].
GET methods: As of Next.js 15, GET handlers are dynamic by default (they run on each request), though in earlier versions they were static by default [citation:4].
Other HTTP methods: POST, PUT, PATCH, DELETE, HEAD, and OPTIONS are never cached, regardless of configuration [citation:10].
Special handlers: sitemap.ts, opengraph-image.tsx, icon.tsx and other metadata files remain static by default unless they use dynamic APIs [citation:3].
Static detection: Next.js determines if a handler can be static based on whether it uses dynamic APIs like cookies(), headers(), or request-specific properties [citation:4].
Route Handlers support ISR-style revalidation, giving you the performance of static generation with periodic updates. You can combine export const dynamic = 'force-static' with either the revalidate segment config or fetch-level next.revalidate options [citation:6]. The lowest revalidate value across the route determines the revalidation frequency [citation:1]. This is particularly useful for data that changes occasionally but doesn't need to be fresh on every request.
For more sophisticated caching strategies, Route Handlers can participate in Next.js's tag-based revalidation system. By adding tags to fetch requests, you can programmatically invalidate cached responses using revalidateTag() from anywhere in your application [citation:1]. This is ideal for CMS-driven content where you want to update the cache immediately when content changes, rather than waiting for a time-based revalidation window.
Check build output: Run next build and examine the route table. '○' indicates static routes, 'λ' indicates dynamic routes [citation:5].
Inspect response headers: Static routes return x-nextjs-cache: HIT or STALE; dynamic routes show MISS or no cache header [citation:7].
Use dynamic APIs intentionally: Adding cookies(), headers(), or accessing request properties automatically opts into dynamic rendering [citation:4].
Check your Next.js version: Versions prior to 15 had GET handlers static by default; 15+ defaults to dynamic [citation:4].
With the introduction of cacheComponents in Next.js 16, route handlers may be executed during build even with dynamic configuration. The recommended pattern is to use await connection() early in your handler to explicitly opt into dynamic behavior while still allowing static verification of the rest of the code [citation:2]. This ensures your handler runs at request time while maintaining build-time safety checks.
Database queries unexpectedly static: ORM queries (Prisma, Drizzle) don't automatically make handlers dynamic. Add export const dynamic = 'force-dynamic' or use headers()/cookies() [citation:4].
Environment variables during build: Static handlers evaluate at build time, so environment variables must be available then [citation:2].
Mixed methods in same file: Only GET methods can be cached; POST/PUT/DELETE remain dynamic even with force-static [citation:10].
Unexpected caching with cron jobs: For scheduled jobs that should run at runtime, use await connection() or headers() to force dynamic execution [citation:2].
You need to create a GET route handler that returns product data. How would you ensure the response is cached for 10 minutes, and what code would you add to make it static?
If you add export const dynamic = 'force-static' to a route handler, what effect does it have on Next.js caching?
What happens if you omit any caching directives in a route handler that fetches data from an external API?
We have a page that calls a route handler to fetch user‑specific data. The handler is currently static, but users see stale data after a profile update. How would you debug and adjust the caching strategy?
During a rollout, a route handler that should be dynamic is returning a 304 Not Modified unexpectedly. What could cause that and how would you fix it?
Explain the trade‑offs between using export const revalidate = 0 versus export const dynamic = 'force-dynamic' in a route handler that serves personalized content.
Design a caching layer for a set of route handlers that serve both public product listings (static) and per‑user cart data (dynamic). How would you configure each handler and what edge‑case considerations are there?
Our CDN is showing stale HTML for a route handler that we switched from static to dynamic. Walk me through the steps you’d take to ensure proper cache invalidation across the Vercel edge network.
When scaling to millions of requests, how does Next.js decide whether to cache a route handler response, and what knobs would you adjust to balance latency and freshness?
We are migrating a monolith API to Next.js route handlers. Some endpoints need to be static for SEO, others must stay dynamic for auth. How would you structure the project, enforce caching policies, and avoid accidental static rendering across teams?
Describe a long‑term strategy for managing cache invalidation and versioning of route handlers in a multi‑team environment, considering CI/CD pipelines and backward compatibility.