WordPress cache integration for Next.js 15
Use the Pantheon cache handler to enable automatic edge cache clearing when WordPress content changes
This guide shows you how to integrate WordPress cache invalidation with a Next.js 15 site on Pantheon. When you publish or update a post in WordPress, a webhook fires and tells Next.js exactly which cached data to refresh — no full rebuild required.
Learning objectives
This tutorial walks you through:
- Setting up
@pantheon-systems/nextjs-cache-handlerin a Next.js 15 project - Tagging WordPress data with surrogate keys using
fetch()cache tags - Creating a revalidation API endpoint that accepts WordPress webhook requests
- Connecting a WordPress mu-plugin that sends webhooks when content changes
- Configuring shared secrets on both sites via Terminus Secrets Manager
This approach works for both Next.js 15 and Next.js 16. The cache handler extracts tags from the internal x-next-cache-tags header on cached page data regardless of version. You do not need to define Surrogate-Key headers in next.config.mjs.
For Next.js 16-specific features like 'use cache' and cacheTag(), see WordPress on-demand revalidation for Next.js 16.
Requirements
- A Next.js 15 site on Pantheon (Quick start)
- A WordPress site on Pantheon
@pantheon-systems/nextjs-cache-handlerversion 0.7.0 or later- Install the following:
- Git
- Terminus*
- Terminus Secrets Manager Plugin (built into Terminus 4.2.0+; earlier versions require the plugin)
* Requires logging in after installation.
Quick start with test upstreams
If you want to skip the manual setup and get a working site immediately, create a Pantheon site from one of the test upstreams that have WordPress cache invalidation pre-configured:
| Upstream | Next.js Version | Cache Strategy |
|---|---|---|
nextjs_15_cache_starter | 15 | ISR + fetch tags — cache handler extracts tags automatically |
nextjs_16_cache_starter | 16 | 'use cache' + cacheTag() — cache handler extracts tags automatically |
These are not core upstreams and are not shown by default in terminus upstream:list. Use the --all flag to find them:
These upstreams include the @pantheon-systems/nextjs-cache-handler package, WordPress REST API integration, surrogate key tagging, and a secured revalidation endpoint. To get end-to-end cache invalidation working:
- Create a site from the upstream.
- Set
WORDPRESS_API_URLto your WordPress site's REST API base URL. - Set a shared
WEBHOOK_SECRETon both the Next.js and WordPress sites. - Install the WordPress mu-plugin on your WordPress site.
- Install the Pantheon Advanced Page Cache plugin on WordPress so that WordPress' CDN caches are cleared appropriately.
The rest of this guide walks through the setup in detail.
How it works
When you tag your fetch() calls with surrogate keys (e.g., post-list, post-123), the @pantheon-systems/nextjs-cache-handler automatically tracks which tags are associated with which pages. When WordPress content changes and revalidateTag() is called, the cache handler clears the affected pages from Pantheon's edge CDN.
No Surrogate-Key headers in next.config.mjs are needed.
Install the cache handler
Configure Next.js
Create a cache-handler.mjs file in your project root:
Reference it in next.config.mjs:
No headers config is needed. The cache handler extracts tags automatically.
Add fetch tags to your data fetching
Tag your fetch() calls with surrogate keys that match what your WordPress mu-plugin sends:
The tags you pass to fetch() via next: { tags: [...] } are automatically propagated by Next.js to the page-level cache. The cache handler extracts them and maps them to the page's URL path.
Create the revalidation endpoint
Create an API route that receives WordPress webhook requests and calls revalidateTag():
When revalidateTag('post-list') is called, the cache handler:
- Looks up the tag mapping:
post-list → ["/blogs", "/blogs/my-post"] - Deletes the cached page entries for those paths
- Calls the edge CDN to purge
/blogsand/blogs/my-post
Connect WordPress webhooks
You need:
-
A WordPress mu-plugin that sends webhooks when content changes. See Add the WordPress mu-plugin.
-
Shared secrets configured on both sites. See Configure secrets.
The surrogate key patterns (post-{id}, post-{slug}, post-list, term-{id}) must match between the WordPress mu-plugin and your fetch() tag values.
Differences from Next.js 16
| Feature | Next.js 15 | Next.js 16 |
|---|---|---|
| Cache tags set via | fetch() with next: { tags } | cacheTag() in 'use cache' functions |
| Tags on page cache | Propagated from fetch tags | Explicitly set via cacheTag() |
| Cache handler config | cacheHandler only | cacheHandler + cacheHandlers.default |
revalidateTag() args | 1 arg: revalidateTag(tag) | 2 args: revalidateTag(tag, { expire: 0 }) |
| Tag granularity | Tags from fetch calls | All cacheTag() values (more granular) |
Both versions use the same cache handler tag extraction mechanism. The main difference is that Next.js 16's cacheTag() gives more granular control over which tags are associated with each page.
Next steps
- WordPress on-demand revalidation for Next.js 16 — Use
cacheTag()for more granular, runtime-driven cache tagging. - Set environment variables
- Deploy to Test and Live environments