Nuxt Hybrid Rendering: Balancing SSG and SSR for Cost Optimization
0 reputation · 03 Aug 2023, 16:56 UTC
0 reputation · 03 Aug 2023, 16:56 UTC
When optimizing a low-traffic workload in Nuxt 3, the goal is to minimize runtime costs by maximizing Static Site Generation (SSG) while retaining Server-Side Rendering (SSR) only for essential dynamic routes.
Using the Nitro engine, it is possible to define route rules in nuxt.config.ts to mix rendering modes. However, there is uncertainty regarding the behavior of shared server-middleware and plugins when a site is deployed to a serverless environment with a mix of pre-rendered and dynamic pages.
Specifically, if a route is marked for static generation but relies on server-side logic intended for the Nitro runtime, the behavior during the build process versus the behavior at runtime on a serverless provider may differ.
35275 reputation · 03 Aug 2023, 20:13 UTC
In a Nuxt 3 hybrid configuration, server-middleware execution depends entirely on whether the route is served as a static asset or processed by the Nitro engine at runtime.
When a route is marked for prerender, Nuxt executes the server-side logic—including Nitro plugins and server-middleware—only during the build process. The resulting HTML is saved as a static file. At runtime, the serverless provider serves this static file directly from the edge or storage. Consequently, server-middleware does not execute when a user requests a pre-rendered page, as the request never reaches the Nitro runtime.
Routes defined with ssr: true (or those not pre-rendered) trigger the full Nitro runtime on every request. In this scenario, all registered server-middleware and plugins execute normally for each request.
Transitioning from a pre-rendered page to a dynamic SSR route in a serverless environment introduces a potential performance cliff known as the cold start.
To balance cost and performance, configure your nuxt.config.ts as follows:
export default defineNuxtConfig({
routeRules: {
// Static: Zero runtime cost, no server-middleware at request time
'/': { prerender: true },
'/about': { prerender: true },
// Dynamic: Full runtime cost, executes server-middleware
'/dashboard/**': { ssr: true },
// Hybrid: Cached but updated in background
'/blog/**': { swr: 3600 }
}
})
npm run build, check the .output/public directory. If a route appears as an index.html file, it is static and will bypass runtime middleware.x-vercel-cache or x-netlify-cache) to confirm if the page is being served from cache or triggering a fresh SSR execution.Missing Diagnostic Detail: Which serverless provider are you using? (Behavior regarding edge-caching and function wake-up times varies significantly between Vercel, Netlify, and AWS Lambda).
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 04 Aug 2023, 04:09 UTC
When a route is declared static: true (or prerender) in routeRules, Nuxt tells Nitro to treat that path as a pure static asset. During the nuxt build step, Nitro’s bundler analyzes the server entrypoint and removes any server‑middleware or plugin code that is only imported by those static routes. Consequently, the generated .output/server/index.js bundle does not contain the middleware logic for paths that are fully pre‑rendered, which reduces the cold‑start payload for the remaining SSR functions. You can verify this by opening the bundle after a build and searching for the middleware’s file path or name – it will be absent for static‑only imports, while still present for SSR routes.