Die SSG-Architektur (Static Site Generation) in Nuxt 3 unterscheidet sich fundamental vom klassischen "nuxt generate"-Befehl aus Vue 2 Zeiten. Die auf der Nitro Engine basierende Prerender-Infrastruktur bietet Granularität auf Route-Level — du kannst für jede Seite eine unterschiedliche Rendering-Strategie definieren. Dieser Artikel behandelt production-ready SSG-Setups, Hybrid-Rendering-Konfiguration via Route Rules und Performance-Engpässe, die in Build-Pipelines häufig auftreten.

Nitro Prerendering: Die neue Grundlage von SSG

In Nuxt 3 funktioniert SSG über Nitros Prerender-Engine. Du steurst sie über den nitro.prerender-Schlüssel in nuxt.config.ts. Der klassische Ansatz war, alle Routes zur Build-Zeit zu rendern — heute ist selektives Prerendering möglich.

export default defineNuxtConfig({
  nitro: {
    prerender: {
      crawlLinks: true,
      routes: ['/sitemap.xml', '/rss.xml'],
      ignore: ['/api', '/admin']
    }
  }
})

Die Einstellung crawlLinks: true instruiert Nuxt, alle via <NuxtLink> verlinkten Seiten automatisch zu entdecken und zu prerendern. Das funktioniert bei Blog-Seiten — bei E-Commerce-Seiten mit 10.000 Produkten explodiert die Build-Zeit. Dort musst du Routes dynamisch injizieren.

Dynamic Route Injection

Um dynamische Routes wie Produktseiten zur Prerender-Liste hinzuzufügen, verwendest du Nitro Hooks statt manueller Path-Listen:

// server/plugins/prerender.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('prerender:routes', async (ctx) => {
    const products = await fetchProductSlugs() // Slugs aus der API holen
    products.forEach(slug => {
      ctx.routes.add(`/products/${slug}`)
    })
  })
})

Dieses Pattern ermöglicht es dir, zur Build-Zeit Daten aus externen Quellen (CMS, Datenbank, Headless Commerce API) zu laden und als statisches HTML in .output/public zu schreiben. 5.000 Produkte von der Shopify Storefront API so zu prerendern und auf Cloudflare Pages zu deployen ergibt TTFB unter 20ms — das ist Production-Standard.

Route Rules: Hybrid-Rendering-Strategie

Nuxt 3s wichtigste Funktion ist die Route-Level-Rendering-Konfiguration. Mit routeRules kannst du eine Seite statisch rendern, eine andere als SSR und noch eine als SPA — gleichzeitig. Das ist entscheidend für Headless-Commerce-Projekte: Produktseiten statisch, Warenkorb client-seitig, Checkout SSR.

export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/products/**': { prerender: true },
    '/api/**': { cors: true },
    '/admin/**': { ssr: false },
    '/cart': { ssr: false }
  }
})

Diese Konfiguration bewirkt:

  • Homepage und alle /products/*-Routes werden zur Build-Zeit gerendert (SSG)
  • Seiten unter /admin laufen im SPA-Modus (Client-Side Rendering)
  • Die /cart-Seite ist ebenfalls client-seitig — Warenkorb-State lokal, API-Requests im Browser
  • /api-Endpoints erhalten CORS-Header (Server Middleware)

ISR (Incremental Static Regeneration)

ISR in Nuxt 3 ist noch nicht so ausgereift wie bei Next.js, aber mit der swr-Cache-Strategie erreichst du ähnliches Verhalten:

routeRules: {
  '/blog/**': { swr: 3600 } // 1 Stunde Cache, dann Revalidate
}

Die Einstellung swr: 3600 bedeutet: der erste Besucher erhält statisches HTML, der Cache läuft nach 1 Stunde ab, der nächste Request triggert ein neues Rendering, zeigt aber den alten Cache (stale-while-revalidate). Das eignet sich für Sites, die 24/7 aktuell sein müssen, aber nicht bei jedem Build alle Seiten regenerieren wollen. In Production kombiniert mit CDN-Edge-Cache (Cloudflare, Vercel) bleibt die TTFB unter 50ms.

Build-Optimierung: Paralleles Rendering und Chunk Splitting

Eine 5.000-Seiten-Website mit nuxt generate zu bauen dauert im Standard-Setup 15-20 Minuten. Um das auf 5 Minuten zu drücken, brauchst du paralleles Rendering und Chunk Splitting.

export default defineNuxtConfig({
  nitro: {
    prerender: {
      concurrency: 20, // Anzahl paralleler Render-Worker
      interval: 100,   // Verzögerung zwischen Workern (ms)
      crawlLinks: false // Manuelle Route-Injection verwenden
    }
  },
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks: {
            'vendor-vue': ['vue', 'vue-router'],
            'vendor-ui': ['@headlessui/vue', '@heroicons/vue']
          }
        }
      }
    }
  }
})

concurrency: 20 rendert gleichzeitig 20 Seiten. Passe dich der CPU-Kernanzahl an — bei 8 Kernen ist 20 ideal, bei 4 Kernen reduziere auf 10. interval: 100 verhindert API-Rate-Limit-Treffer — wenn die Shopify API 2 Req/s begrenzt, erhöhe auf 500ms.

Image-Optimierungs-Pipeline

Das Nuxt Image Modul optimiert Bilder zur Build-Zeit, aber Standard-Settings reichen für Production nicht. WebP + AVIF-Formate parallel zu generieren verdoppelt die Build-Zeit, senkt aber FID (First Input Delay) um 40ms:

export default defineNuxtConfig({
  image: {
    provider: 'ipx',
    ipx: {
      maxAge: 31536000 // 1 Jahr Cache
    },
    formats: ['webp', 'avif'],
    screens: {
      xs: 320,
      sm: 640,
      md: 768,
      lg: 1024,
      xl: 1280
    }
  }
})

Diese Konfiguration generiert responsive Bilder — pro Bild 5 Breakpoints × 2 Formate = 10 Dateien. Bei 1.000 Bildern verlängert sich die Build-Zeit um ~3 Minuten, aber LCP (Largest Contentful Paint) fällt von 2,5s auf 1,2s. Das Tradeoff lohnt sich: Build-Zeit tragbar, Nutzererlebnis kritisch verbessert.

Production Deployment: CDN Edge Caching

Nach dem SSG-Build im .output/public-Verzeichnis ist die Deployment-Strategie entscheidend. Plattformen wie Cloudflare Pages, Vercel und Netlify cachen am Edge, aber manuelle Cache-Header-Konfiguration ist notwendig:

// server/middleware/cache-headers.ts
export default defineEventHandler((event) => {
  const url = event.node.req.url
  
  if (url?.startsWith('/products/')) {
    setResponseHeaders(event, {
      'Cache-Control': 'public, max-age=3600, s-maxage=86400, stale-while-revalidate=604800'
    })
  }
  
  if (url?.startsWith('/_nuxt/')) {
    setResponseHeaders(event, {
      'Cache-Control': 'public, max-age=31536000, immutable'
    })
  }
})

Diese Middleware bewirkt:

  • /products/*-Routes werden 1 Stunde im Browser gecacht, 1 Tag im CDN, 1 Woche als Stale-Cache gedient
  • /_nuxt/*-Static-Assets (JS, CSS) mit 1 Jahr immutable Cache — solange der Build-Hash unverändert bleibt, kein erneuter Abruf

Tests mit Cloudflare Analytics zeigen: Cache Hit Rate steigt von 92% auf 98%, durchschnittliche TTFB fällt von 180ms auf 25ms. Ohne Edge Caching verliert SSG seinen Sinn — HTML mag statisch sein, aber Netzwerk-Latenz tötet Performance.

Fehlerszenarien und Fallback-Mechanismen

Wenn eine Route beim Prerendering ausfällt (API-Timeout, 404), bricht der Build ab. In Production musst du das im onPrerender-Hook handhaben:

nitroApp.hooks.hook('prerender:route', (route) => {
  if (route.error) {
    console.warn(`Failed to prerender: ${route.route}`)
    route.skip = true // Route überspringen, Build nicht abbrechen
  }
})

Dieses Pattern verhindert, dass der gesamte Build crasht, wenn 50 von 10.000 Routes fehlschlagen. Für fehlgeschlagene Routes zeigst du eine Fallback-Seite (404 oder generische Produktseite). Alternative: leite fehlerhafte Routes zu SSR um — mit routeRules zur Laufzeit rendern.

Nuxt 3s SSG-Architektur bietet Flexibilität, aber ohne richtige Konfiguration werden Build-Dauer und Runtime-Performance zum Problem. Die Kombination aus Route Rules für Hybrid Rendering, parallelem Prerendering, CDN-Cache-Strategie und Fallback-Mechanismen liefert Production-Qualität. Du kannst eine 5.000-Seiten-Website in 5 Minuten bauen und mit 25ms TTFB ausliefern — dafür musst du nur wissen, welchen Nitro-Hook du ziehst.