Joey Teng SiteYour SUPER-powered WP Engine Site

Cache Invalidation StrategiesRevalidation hooks, stale-while-revalidate, and beyond.

Caching can be a major performance booster in your web stack. But caching is also a two-sided coin: once content is cached, you need a way to keep it in sync with updates or changes. That’s where cache invalidation strategies come in. In this article, we’ll explore three major angles to handling cache invalidation in […]

0
1
Cache Invalidation StrategiesRevalidation hooks, stale-while-revalidate, and beyond.

Caching can be a major performance booster in your web stack. But caching is also a two-sided coin: once content is cached, you need a way to keep it in sync with updates or changes. That’s where cache invalidation strategies come in.

In this article, we’ll explore three major angles to handling cache invalidation in a Next.js and headless WordPress setup (though the same concepts apply more broadly too):

  1. Revalidation and Build Hooks
  2. Stale-While-Revalidate Patterns
  3. Manual vs. Automatic Invalidation

1. Revalidation and Build Hooks

Triggering Next.js Rebuilds or Revalidation When WordPress Content Updates

A hallmark feature of Next.js is Incremental Static Regeneration (ISR), which allows you to define how frequently a static page should be re-generated in the background. But sometimes you want even tighter control—like, whenever new content is published in WordPress. In that case, you can set up a webhook in WordPress to “ping” Next.js whenever new or updated content goes live.

How might that look in code?

WordPress Side (Webhook Setup)

If you’re using WordPress, you can leverage its built-in hooks or a plugin to send a request to a custom Next.js API route. For example, you might set up a small snippet in a plugin that triggers an HTTP POST to your Next.js endpoint every time a post is published.

// functions.php (or custom plugin)
add_action('publish_post', 'notify_nextjs_of_new_post');

function notify_nextjs_of_new_post($post_id) {
    $url = 'https://your-nextjs-site.com/api/revalidate-post';
    $body = array('postId' => $post_id);
    
    wp_remote_post($url, array(
      'method' => 'POST',
      'headers' => array('Content-Type' => 'application/json'),
      'body' => wp_json_encode($body),
    ));
}

Next.js Side (API Route)

Then, in Next.js, create an API route (e.g., /pages/api/revalidate-post.js) to handle that incoming request:

// pages/api/revalidate-post.js

export default async function handler(req, res) {
  try {
    const { postId } = req.body;
    // We assume there's a dynamic route like /posts/[id] using getStaticProps
    await res.revalidate(`/posts/${postId}`);
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send('Error revalidating');
  }
}

With that in place, whenever WordPress publishes a new post, WordPress pings your Next.js endpoint, Next.js calls res.revalidate(), and—voilà—your cached page gets updated.

The Upsides

  • Instant Updates: No waiting for the next scheduled revalidation; your content goes live as soon as the webhook fires.
  • Lower Build Times: You’re not rebuilding your entire site, just revalidating a specific page (or set of pages).

The Downsides

  • Complexity: Setting up webhooks and matching them to specific routes can get tricky if your site structure is complex.
  • Security Considerations: Make sure your revalidation endpoint is protected or at least not publicly guessable.

2. Stale-While-Revalidate Patterns

Stale-While-Revalidate is a strategy that serves cached data immediately, then fetches fresh data behind the scenes. This pattern is popular in SSR scenarios or client-side data fetching libraries like SWR and React Query.

How It Works

Let’s say you have a page that fetches dynamic data on every request. By default, SSR always grabs the latest data from your database or API. With a stale-while-revalidate approach, you might store the data in a cache (like an in-memory cache or Redis) and serve it instantly while you make a fresh request in the background. If that fresh request returns updated data, you replace the cache so the next visitor sees the new version.

Example Using SWR (Client-Side)

import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

function MyPosts() {
  const { data, error, isLoading } = useSWR('/api/posts', fetcher, {
    revalidateOnFocus: false,
    revalidateIfStale: true,
  });

  if (error) return <div>Error loading posts</div>;
  if (isLoading) return <div>Loading...</div>;

  return (
    <ul>
      {data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Here’s what happens:

  1. Initial Load: SWR checks if there’s cached data for /api/posts. If so, it shows that instantly (stale data).
  2. Background Fetch: SWR calls the fetcher to see if there’s newer data.
  3. Cache Update: If new data arrives, SWR updates the UI automatically.

Why Use Stale-While-Revalidate?

  • Fast Initial Response: Users see cached content right away without waiting for a fresh fetch.
  • Smooth Updates: Content updates quietly in the background.
  • Versatility: Works great on both the client side (e.g., SWR) and the server side (custom SSR with caching).

Caveats

  • Potential Stale Content: If your data changes frequently, some users might see old content for a few seconds.
  • Complex Caching Logic: You need a plan for how long data can stay “stale,” how often to recheck, etc.

3. Manual vs. Automatic Invalidation

Manual Invalidation

Manual invalidation typically means you or your content editors press a button or run a command to clear caches or force a rebuild. For instance, your WordPress editor might open a plugin’s interface and click “Clear Cache” after publishing a big update.

  • Pros: Fine-grained control; ensures you only invalidate when necessary.
  • Cons: Easy to forget. If the site has many content managers, ensuring everyone follows the same process can be challenging.

Automatic Invalidation

Automatic invalidation means the system handles it for you, whether via:

  • Scheduled Cron Jobs: Clear or rebuild cache every hour.
  • Webhooks: As covered earlier, triggers on content publish/update.
  • API Responses: If your data source sends a push notification whenever data changes.
  • Pros: Less chance of stale data. Fewer manual steps.
  • Cons: Potential over-invalidation (you might rebuild more often than necessary), added complexity in setting up triggers.

Best Practices for High-Traffic Sites

  1. Pick a Consistent Strategy
    Decide if you want to rely on timed intervals (like revalidate in Next.js) or event-based triggers (like WordPress webhooks). Mixing too many approaches can cause confusion or missed invalidations.
  2. Avoid Over-Invalidation
    If you have a large site, invalidating too many pages too often can lead to performance bottlenecks. Use targeted revalidation (e.g., revalidate a specific route after a post update).
  3. Security Matters
    If you create an API endpoint for revalidation (like /api/revalidate-post), lock it down. Basic auth, tokens, or secret keys can help ensure only trusted sources can trigger a rebuild.
  4. Monitor Logs & Performance
    Keep an eye on how often revalidation happens and how long rebuilds or cache flushes take. If your logs show an abnormal spike in requests to your revalidation endpoint, it might indicate a misconfiguration or malicious use.

Conclusion

Cache invalidation is often considered the trickiest part of caching, and it’s easy to see why. Balancing immediacy, performance, and complexity requires a bit of planning:

  • Revalidation and Build Hooks: Event-based triggers for content that must go live right away.
  • Stale-While-Revalidate: A best-of-both-worlds approach that gives users instant responses while updating behind the scenes.
  • Manual vs. Automatic Invalidation: Sometimes manual is simpler for small sites, while automation scales better for larger or more frequently updated content.

As always, the right strategy depends on your site’s traffic, how often your content changes, and the tolerance you (and your users) have for slightly out-of-date data. Experiment, measure, refine—and you’ll find a cache invalidation approach that keeps both your users and your servers happy. Happy caching!

J
WRITTEN BY

Joey Teng

Responses (0 )