Deploy

Static Site Generation

Deploy your WPNuxt site as a fully static site

WPNuxt supports full static site generation, pre-rendering all pages at build time. This produces static HTML files that can be deployed to any CDN or static hosting provider.

When to Choose SSG

ConsiderationSSGSSR
Content updatesRequires rebuildInstant (with SWR cache)
HostingAny static host, no server neededRequires Node.js
PerformanceFastest TTFB (~10-50ms from CDN)Fast with caching (~50-200ms)
Build timeGrows with content volumeN/A
Preview supportLimitedFull support
CostCheapest (static hosting)Depends on compute

Choose SSG for blogs, documentation, and marketing sites where content changes infrequently and you can trigger rebuilds on publish.

Choose SSR for sites with many pages, frequent updates, preview requirements, or authenticated content.

Configuration

Set the Nitro preset to static and configure pre-rendering:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@wpnuxt/core'],

  nitro: {
    preset: 'static',
    prerender: {
      concurrency: 10,   // Parallel route fetches
      interval: 1000,     // Delay between batches (avoid overwhelming WordPress)
      failOnError: false, // Don't fail build on individual route errors
      routes: ['/'],      // Seed route for crawling
    },
  },

  wpNuxt: {
    wordpressUrl: 'https://your-wordpress.com',
  },
})

Pre-rendering WordPress Routes

Nuxt's crawler discovers routes by following links from the seed routes. For WordPress sites, it's more reliable to fetch all routes explicitly using the prerender:routes hook:

nuxt.config.ts
const WORDPRESS_URL = 'https://your-wordpress.com'
const GRAPHQL_ENDPOINT = '/graphql'

export default defineNuxtConfig({
  modules: ['@wpnuxt/core'],

  nitro: {
    preset: 'static',
    prerender: {
      concurrency: 10,
      interval: 1000,
      failOnError: false,
      routes: ['/'],
    },
  },

  hooks: {
    async 'prerender:routes'(ctx) {
      await fetchWordPressRoutes(ctx.routes)
    },
  },

  wpNuxt: {
    wordpressUrl: WORDPRESS_URL,
  },
})

async function fetchWordPressRoutes(routes: Set<string>) {
  console.log('[wpnuxt] Fetching WordPress routes for prerendering...')

  try {
    const response = await fetch(`${WORDPRESS_URL}${GRAPHQL_ENDPOINT}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        query: `
          query AllContentForPrerender {
            posts(first: 100) { nodes { uri } }
            pages(first: 100) { nodes { uri } }
          }
        `,
      }),
    })

    const data = await response.json() as {
      data?: {
        posts?: { nodes: Array<{ uri: string }> }
        pages?: { nodes: Array<{ uri: string }> }
      }
    }

    const posts = data.data?.posts?.nodes || []
    const pages = data.data?.pages?.nodes || []

    for (const post of posts) {
      if (post.uri) routes.add(post.uri)
    }
    for (const page of pages) {
      if (page.uri) routes.add(page.uri)
    }

    console.log(`[wpnuxt] Added ${posts.length} posts and ${pages.length} pages`)
  } catch (error) {
    console.warn('[wpnuxt] Failed to fetch WordPress routes:', error)
  }
}
If you have more than 100 posts or pages, use GraphQL pagination (after cursor) to fetch all content, or increase the first parameter.

URI Normalization

WordPress URIs typically include trailing slashes (e.g., /hello-world/). This is important for SSG because the generated file path depends on the URI:

  • /hello-world/ generates hello-world/index.html
  • /hello-world generates hello-world.html

Ensure consistency between your WordPress permalink structure and your Nuxt routing. The prerender:routes hook above uses URIs directly from WordPress, maintaining the correct format.

If you're using useNodeByUri() with route.path, note that Nuxt's route.path does not include a trailing slash by default. You may need to normalize:

const uri = route.path.endsWith('/') ? route.path : `${route.path}/`
const { data } = await useNodeByUri({ uri })

CI/CD Considerations

Schema Download

The GraphQL schema must be available at build time. In CI environments where WordPress may not be accessible:

nuxt.config.ts
const IS_CI = process.env.CI === 'true'

export default defineNuxtConfig({
  wpNuxt: {
    wordpressUrl: 'https://your-wordpress.com',
    downloadSchema: !IS_CI, // Skip in CI, commit schema.graphql instead
  },
})

If you skip schema download in CI, commit schema.graphql to your repository.

Route Fetching

The prerender:routes hook fetches routes from WordPress at build time. Ensure WordPress is accessible from your CI environment:

nuxt.config.ts
const IS_CI = process.env.CI === 'true'
const IS_VERCEL = !!process.env.VERCEL

export default defineNuxtConfig({
  hooks: {
    async 'prerender:routes'(ctx) {
      // Fetch on Vercel (has internet) or locally, skip in restricted CI
      if (IS_VERCEL || !IS_CI) {
        await fetchWordPressRoutes(ctx.routes)
      }
    },
  },
})

Environment Variables

Set WPNUXT_WORDPRESS_URL in your CI/CD environment:

# GitHub Actions example
env:
  WPNUXT_WORDPRESS_URL: https://your-wordpress.com

Deploying

Vercel

Vercel automatically detects static Nuxt sites. See the Vercel deployment guide for details.

vercel.json
{
  "buildCommand": "pnpm run build",
  "outputDirectory": ".output/public"
}

Netlify

netlify.toml
[build]
  command = "pnpm run build"
  publish = ".output/public"

Cloudflare Pages

wrangler.toml
[site]
  bucket = ".output/public"

Or configure in the Cloudflare dashboard:

  • Build command: pnpm run build
  • Build output directory: .output/public

Any Static Host

Run pnpm run build and upload the .output/public/ directory to any static hosting provider (AWS S3, GitHub Pages, Firebase Hosting, etc.).

Triggering Rebuilds

Since static sites don't update automatically, trigger a rebuild when content changes in WordPress:

  1. WordPress webhook — Use a plugin like WP Webhooks to send a POST request to your hosting provider's deploy hook when content is published
  2. Scheduled builds — Configure your CI to rebuild on a schedule (e.g., every hour)
  3. Manual — Trigger a rebuild from your hosting provider's dashboard

Most hosting providers (Vercel, Netlify, Cloudflare) support deploy hooks — a URL that triggers a new build when called.

Copyright © 2026