Guide

Caching

Optimize performance with multi-layer caching

Every composable call triggers a network request to your WordPress GraphQL API. Without caching, each page load costs ~200-500ms waiting for WordPress to respond. With caching enabled (the default), repeated requests resolve in ~1-5ms — making your site feel instant.

WPNuxt provides multiple layers of caching to achieve this. See How WPNuxt Works for where caching fits in the overall architecture.

Cache Layers

LayerScopeDurationPurpose
Server (Nitro)All usersConfigurableCache GraphQL responses server-side
Client (GraphQL)Per browserSessionDeduplicate identical queries during navigation
PayloadPer requestHydrationPrevent refetch after SSR

All layers work together automatically.

Server-Side Caching

WPNuxt caches GraphQL responses server-side using Nitro route rules.

How It Works

  1. First request fetches from WordPress (~200-500ms)
  2. Response is cached on the server
  3. Subsequent requests use cache (~1-5ms)
  4. With SWR enabled, stale content serves instantly while refreshing in background

Configuration

nuxt.config.ts
wpNuxt: {
  cache: {
    enabled: true,   // Default: true
    maxAge: 300,     // Default: 300 (5 minutes)
    swr: true        // Default: true
  }
}
OptionDefaultDescription
enabledtrueEnable/disable caching
maxAge300Cache duration in seconds
swrtrueStale-while-revalidate

Client-Side Caching

WPNuxt enables client-side GraphQL caching by default. This deduplicates identical queries during client-side navigation.

Per-Query Control

// Disable client caching for real-time data
const { data } = usePosts(undefined, { clientCache: false })

// Custom cache key for complex scenarios
const { data } = usePosts({ category: 'news' }, {
  cacheKey: `posts-news-${locale.value}`
})
OptionTypeDefaultDescription
clientCachebooleantrueEnable client-side caching
cacheKeystring-Custom cache key suffix

Payload Caching (getCachedData)

WPNuxt optimizes SSR payload handling with a smart getCachedData implementation:

  • During hydration: Uses payload data (no refetch needed)
  • Watch-triggered refetches: Uses cached data when available
  • Manual refresh: Always fetches fresh data

Custom getCachedData

Override the default behavior for specific use cases:

const { data } = usePosts(undefined, {
  getCachedData: (key, nuxtApp, ctx) => {
    // Always fetch fresh
    if (ctx.cause === 'refresh:manual') {
      return undefined
    }
    // Use cached data
    return nuxtApp.payload.data[key]
  }
})

The ctx.cause can be:

  • 'initial' - First load
  • 'refresh:manual' - Called refresh()
  • 'refresh:hook' - Triggered by Nuxt hook
  • 'watch' - Reactive dependency changed

Data Freshness Trade-offs

Caching makes your site fast, but it introduces a fundamental trade-off: cached content may be stale. Understanding this trade-off helps you choose the right caching strategy.

With SWR enabled (default)

When a visitor requests a page, they immediately receive the cached version — even if it's older than maxAge. In the background, the server fetches fresh data from WordPress and updates the cache. The next visitor sees the updated content.

This means content can be up to maxAge seconds old (5 minutes by default). For most content sites, this is an excellent trade-off: visitors never wait for WordPress, and content stays reasonably fresh.

Without SWR

When cache expires, the next visitor waits for a fresh WordPress response (~200-500ms) before seeing the page. Content is never older than maxAge, but some visitors experience slower page loads.

Preview mode

Preview mode automatically bypasses all caching — editors always see the latest draft content. See Preview Mode for setup details.

Choosing the right balance

For most WordPress content sites, the default configuration (maxAge: 300, swr: true) provides the best balance of speed and freshness. Content updates appear within 5 minutes, and visitors never wait for WordPress. If your site requires near-real-time updates, reduce maxAge or disable caching for specific queries. See Recommended Strategies below for common configurations.

When to Disable Caching

Development

// See changes immediately
wpNuxt: {
  cache: {
    enabled: false
  }
}

Real-Time Content

// Per-query: disable for live data
const { data } = useLiveComments(undefined, { clientCache: false })

// Global: short cache with no SWR
wpNuxt: {
  cache: {
    maxAge: 60,
    swr: false
  }
}

Authenticated Content

Authenticated requests bypass server cache automatically (via Authorization header). For user-specific data, disable client caching. See also Preview Mode for how caching interacts with draft content previews.

const { data } = useViewer(undefined, { clientCache: false })

High-Traffic Sites

wpNuxt: {
  cache: {
    maxAge: 3600,  // 1 hour
    swr: true
  }
}
Site TypemaxAgeswrclientCacheWhy
Blog / content site300–3600truetrueContent changes infrequently, SWR keeps it fresh
News / frequently updated60truetrueShort cache + SWR = fresh content without latency
Dashboard / real-time0falsefalseAlways fetch latest data
E-commerce catalog300truetrueProduct data is mostly stable

Cache Invalidation

Manual Refresh

const { data, refresh } = usePosts()

// Force fresh data
await refresh()

Reactive Invalidation

const category = ref('news')
const { data } = usePosts(
  { categoryName: category },
  { watch: [category] }
)

// Changing category triggers refetch
category.value = 'tech'

Webhook Revalidation {#webhook-revalidation}

WPNuxt can automatically purge the server cache when content changes in WordPress. When configured, WordPress sends a POST request to your Nuxt app, which clears all cached GraphQL responses immediately — no waiting for maxAge to expire.

Setup

1. Configure the revalidation secret

Set the WPNUXT_REVALIDATE_SECRET environment variable (or configure it in nuxt.config.ts):

nuxt.config.ts
wpNuxt: {
  cache: {
    revalidateSecret: process.env.WPNUXT_REVALIDATE_SECRET
  }
}

This registers a POST endpoint at /api/_wpnuxt/revalidate.

2. Install the WordPress mu-plugin

Copy wpnuxt-revalidate.php to your WordPress mu-plugins/ directory and add to wp-config.php:

wp-config.php
define('WPNUXT_FRONTEND_URL', 'https://your-nuxt-app.com');
define('WPNUXT_REVALIDATE_SECRET', 'your-secret');

The plugin automatically notifies your Nuxt app when posts are created, updated, or deleted.

3. Vercel CDN purge (Vercel deployments only)

On Vercel, GraphQL responses are cached at the CDN edge. WPNuxt tags all cached responses with Vercel-Cache-Tag: wpnuxt, enabling targeted cache purging via Vercel's tag invalidation API.

To enable CDN cache purging on revalidation, add a VERCEL_TOKEN environment variable in your Vercel project settings:

VariableHow to get it
VERCEL_TOKENCreate at vercel.com/account/tokens

VERCEL and VERCEL_PROJECT_ID are provided automatically by Vercel at runtime. When VERCEL_TOKEN is set, the revalidation endpoint invalidates the wpnuxt cache tag, purging all cached GraphQL responses from Vercel's CDN.

Testing

curl -X POST https://your-site.com/api/_wpnuxt/revalidate \
  -H 'Content-Type: application/json' \
  -d '{"secret":"your-secret"}'

# Expected: {"success":true,"purged":0,"vercelPurged":true}
# Wrong secret: 401

Server Cache Expiry

Server cache also expires automatically based on maxAge. With webhook revalidation configured, content updates appear immediately instead of waiting up to maxAge seconds.

Copyright © 2026