Google can render JavaScript. But it's slower and less reliable than HTML. Choose the wrong rendering mode and your content might not get indexed.
Nuxt defaults to SSR (Server-Side Rendering). Every page generates HTML on the server by default. You can override this per route using routeRules.
SSR (Server-Side Rendering) - Server sends HTML, JavaScript hydrates (default)
SSG (Static Site Generation) - Pre-render HTML at build time
SPA (Single Page Application) - JavaScript renders everything client-side
Hybrid - Mix modes per route using routeRulesISR (Incremental Static Regeneration) - Serve cached HTML, rebuild in background
Default behavior in Nuxt. Server generates HTML on each request. Google gets immediate content.
export default defineNuxtConfig({
ssr: true // This is already the default
})
✅ Good for:
❌ Don't use for:
Performance note: SSR requires server compute on every request. SSG serves pre-built files from CDN.
Pre-render routes at build time. Run nuxt generate or configure prerendering in nuxt.config.ts:
export default defineNuxtConfig({
nitro: {
prerender: {
routes: ['/blog', '/docs', '/about']
}
}
})
Or use routeRules for wildcard patterns:
export default defineNuxtConfig({
routeRules: {
'/blog/**': { prerender: true },
'/docs/**': { prerender: true }
}
})
✅ Good for:
❌ Don't use for:
Build consideration: A site with 50,000 routes might take 30+ minutes to build. Use SSR or ISR instead.
Disable SSR for client-only rendering:
export default defineNuxtConfig({
ssr: false
})
Or per route:
export default defineNuxtConfig({
routeRules: {
'/admin/**': { ssr: false }
}
})
Google's crawler downloads JavaScript, waits for execution, then indexes. This adds 5-10 seconds to indexing time.
Use it for: Internal dashboards, apps behind authentication, content that shouldn't be indexed.
Avoid it for: Marketing pages, blog posts, product catalogs—anything you want Google to index quickly.
Common mistake: Launching a SPA site expecting Google to index it immediately. You'll wait weeks longer than SSR.
Mix rendering modes based on route needs using routeRules:
export default defineNuxtConfig({
routeRules: {
// Prerender static content at build time
'/': { prerender: true },
'/blog/**': { prerender: true },
'/docs/**': { prerender: true },
// SSR for dynamic content
'/user/**': { ssr: true },
'/dashboard/**': { ssr: true },
// SPA for auth-only sections
'/admin/**': { ssr: false },
// ISR: Cache with revalidation
'/products/**': {
swr: 3600 // Revalidate every hour
},
// ISR with longer cache
'/news/**': {
swr: 86400 // Revalidate daily
}
}
})
This is Nuxt's most powerful feature for SEO. You get SSG speed for static content, SSR flexibility for dynamic content, and SPA efficiency for authenticated sections—all in one app.
Serve cached HTML, rebuild in background. Best of SSG speed + SSR freshness:
export default defineNuxtConfig({
routeRules: {
'/blog/**': {
swr: 3600 // Cache for 1 hour, rebuild in background
}
}
})
Google sees instant HTML (like SSG), but content stays fresh (like SSR).
Use it for:
How it works:
Don't guess. Check what Googlebot actually received:
1. Google Search Console URL Inspection
If the HTML tab shows <div id="__nuxt"></div> with no content, Google isn't seeing your page.
2. View Page Source
Right-click page > "View Page Source" (not Inspect Element)
✅ Good: Full content in HTML ❌ Bad: Empty div with JavaScript
3. Fetch as Google
curl -A "Mozilla/5.0 (compatible; Googlebot/2.1)" https://yoursite.com
Should return complete HTML with content.
4. Check Build Output
When running nuxt build, look for prerendered routes:
ℹ Prerendering 42 initial routes with crawler
├── /
├── /blog/post-1
├── /blog/post-2
└── ...
If routes you expect to be prerendered aren't listed, check your routeRules configuration.
Mistake 1: Using SPA for a blog You're making Google work 10x harder. SSG renders in milliseconds.
Mistake 2: SSR for static documentation Why run server compute for content that never changes? SSG is free to serve.
Mistake 3: SSG for 100,000 product pages Your builds will time out. Use SSR or ISR instead.
Mistake 4: Forgetting route rules
Nuxt defaults to SSR. Without explicit routeRules, builds won't prerender anything—you'll need a server for all requests.
Mistake 5: Not testing the output Always verify with Search Console. Your local dev server lies.
Mistake 6: Mixing conflicting route rules More specific patterns override general ones. Order matters:
// ❌ Wrong: Specific rule comes after general
routeRules: {
'/blog/**': { prerender: true },
'/blog/draft': { ssr: false } // Won't work, already matched above
}
// ✅ Right: Specific before general
routeRules: {
'/blog/draft': { ssr: false },
'/blog/**': { prerender: true }
}
Start with this configuration for content-heavy sites:
export default defineNuxtConfig({
routeRules: {
// Static marketing pages
'/': { prerender: true },
'/about': { prerender: true },
'/contact': { prerender: true },
// Blog and docs
'/blog/**': { prerender: true },
'/docs/**': { prerender: true },
// Products with ISR
'/products/**': {
swr: 3600 // Fresh within 1 hour
},
// User-specific content
'/dashboard/**': { ssr: true },
'/account/**': { ssr: true },
// Admin panel (no SEO needed)
'/admin/**': { ssr: false }
}
})
Adjust based on your content update frequency.
When using @nuxt/content, enable crawler to auto-discover routes:
export default defineNuxtConfig({
nitro: {
prerender: {
crawlLinks: true,
routes: ['/']
}
}
})
Nuxt will crawl your site starting from /, discovering all <NuxtLink> references and prerendering them automatically.
For dynamic routes:
export default defineNuxtConfig({
nitro: {
prerender: {
routes: async () => {
// Generate routes from content
const { $content } = useNuxtApp()
const posts = await $content('blog').find()
return posts.map(post => `/blog/${post.slug}`)
}
}
}
})
Different hosts support different rendering modes:
| Platform | SSR | SSG | ISR/SWR |
|---|---|---|---|
| Netlify | ✅ | ✅ | ✅ (via functions) |
| Vercel | ✅ | ✅ | ✅ (ISR built-in) |
| Cloudflare Pages | ✅ | ✅ | ✅ (via KV) |
| Static hosts (S3, GitHub Pages) | ❌ | ✅ | ❌ |
| Node.js server | ✅ | ✅ | ⚠️ (needs cache layer) |
Choose your rendering strategy based on your hosting platform.