Skip to content

Strapi 5 webhook to invalidate Vercel CDN cache tags for Next.js pages

Learn step‑by‑step how to configure a Strapi 5 webhook that triggers Vercel’s tag‑based CDN cache invalidation, using Next.js cache tags and the Vercel edge‑cache API.

By the Revisionary team · · 4 min read

group of people having a meeting
Photo by Mario Gogh on Unsplash

A Strapi 5 webhook can call Vercel’s edge‑cache API to purge cache tags whenever content is created, updated, or published, keeping your Next.js pages fresh without a full redeploy. Below is a complete guide that covers webhook configuration, secure handling, Next.js tag assignment, and Vercel tag invalidation.

Overview of the workflow

  1. Assign cache tags in Next.js – each page that depends on a Strapi entry adds a Vercel-Cache-Tag (via cacheTag).
  2. Configure a Strapi webhook – listen to entry.update and entry.publish events for the relevant content types.
  3. Create a secure Next.js API route – receives the webhook payload, verifies it, and calls Vercel’s edge‑cache/invalidate‑by‑tags endpoint.
  4. Optionally use Revisionary – if you need per‑version tags, Revisionary can provide the entry version ID to build unique tags.

The result is on‑demand revalidation: when an editor updates an article in Strapi, the associated Next.js page’s cache tag is invalidated and the next request fetches fresh data.


black flat screen computer monitor
Photo by Mohammad Rahmani on Unsplash

1. Tagging Next.js pages with cacheTag

Next.js 15 (and earlier 13/14) provides the cacheTag function to attach one or more tags to a cached response. The tag is sent as the Vercel-Cache-Tag response header, which Vercel’s CDN stores.

tsx
// app/articles/[slug]/page.tsx
import { cacheTag } from 'next/cache';
import { fetchArticle } from '@/lib/api';

export default async function ArticlePage({ params }: { params: { slug: string } }) {
  'use server';

  // Tag the response with the article ID – useful for invalidation
  cacheTag(`article-${params.slug}`);

  const article = await fetchArticle(params.slug);
  return (
    <article>
      <h1>{article.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: article.body }} />
    </article>
  );
}

When the page renders, Next.js adds Vercel-Cache-Tag: article‑my‑post to the response. Any request that hits Vercel’s CDN will store the response under that tag.

1.1 Multiple tags

If a page combines several content sources (e.g., author profile + article), you can add multiple tags:

ts
cacheTag(['article-123', 'author-45']);

2. Setting up the Strapi 5 webhook

Strapi 5 lets you define global webhook defaults in ./config/server.js (or server.ts). Define a bearer token for authentication and point the webhook to a Next.js API route.

js
// config/server.js
module.exports = {
  webhooks: {
    defaultHeaders: {
      Authorization: `Bearer ${process.env.WEBHOOK_TOKEN}`,
    },
    // Optional: verify payload signatures with X‑Webhook‑Signature
  },
};

2.1 Create the webhook in the admin UI

  1. Settings → Webhooks → Create new webhook
  2. URL: https://my-next-app.vercel.app/api/strapi-revalidate
  3. Events: select Entry.update and Entry.publish for the content types you want to invalidate (e.g., Article). You can also add Entry.delete if you want to purge removed pages.
  4. Headers: the Authorization header is added automatically from defaultHeaders.
  5. Save.

Strapi will now POST a JSON payload to the specified URL whenever the selected events occur.


3. Secure Next.js webhook handler

Create an API route (or a Route Handler in the App Router) that verifies the request and calls Vercel’s edge‑cache API.

ts
// src/app/api/strapi-revalidate/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Vercel } from '@vercel/sdk';

export async function POST(req: NextRequest) {
  // 1️⃣ Verify bearer token
  const auth = req.headers.get('authorization');
  if (auth !== `Bearer ${process.env.WEBHOOK_TOKEN}`) {
    return new NextResponse('Unauthorized', { status: 401 });
  }

  // 2️⃣ Parse Strapi payload
  const payload = await req.json();
  const { event, entry } = payload;

  // We only care about updates and publishes
  if (!['entry.update', 'entry.publish'].includes(event)) {
    return new NextResponse('Ignored event', { status: 200 });
  }

  // 3️⃣ Build the cache tag – match the tag used in Next.js
  // Assuming the entry ID is the slug or a numeric ID
  const tag = `article-${entry.id}`; // adjust if you use slug

  // 4️⃣ Call Vercel edge‑cache invalidate‑by‑tags API
  const vercel = new Vercel({
    bearerToken: process.env.VERCEL_BEARER_TOKEN,
  });

  try {
    await vercel.edgeCache.invalidateByTags({
      projectIdOrName: process.env.VERCEL_PROJECT,
      tags: [tag],
    });
    return new NextResponse('Cache invalidated', { status: 200 });
  } catch (err) {
    console.error('Vercel invalidate error', err);
    return new NextResponse('Failed to invalidate', { status: 500 });
  }
}

Key security points

  • Bearer token – matches the token set in Strapi’s defaultHeaders.
  • Optional HMAC verification – Strapi can send an X‑Webhook‑Signature header; verify it with a shared secret for extra protection.
  • Limit allowed events – the handler explicitly ignores unrelated events.

3.1 Environment variables

Add the following to your Vercel project’s environment:

  • WEBHOOK_TOKEN – shared secret for Strapi ↔ Next.js.
  • VERCEL_BEARER_TOKEN – token generated in Vercel’s dashboard (Settings → Tokens).
  • VERCEL_PROJECT – your Vercel project name or ID.

4. Using Revisionary for version‑aware tags (optional)

If you version content with the Revisionary plugin, you may want to invalidate the cache only when a specific version is published. Revisionary stores each version in the audit log and provides a content API that returns version metadata.

ts
// Example: fetch the latest published version ID
const res = await fetch(`${process.env.STRAPI_URL}/api/articles/${entry.id}?populate=revision`,
  { headers: { Authorization: `Bearer ${process.env.STRAPI_ADMIN_TOKEN}` } }
);
const data = await res.json();
const versionId = data.data.attributes.revision?.latest?.id;
const tag = `article-${entry.id}-v${versionId}`;

Then use the same tag in your Next.js page:

ts
cacheTag(`article-${slug}-v${process.env.NEXT_PUBLIC_VERSION_ID}`);

This approach lets you keep older cached versions available while the new version is being reviewed, and only purge the tag when the revision is finally published.


5. Testing the end‑to‑end flow

  1. Deploy the Next.js app – make sure the /api/strapi-revalidate route is reachable.
  2. Create a test article in Strapi.
  3. Open the article page in a browser; inspect the response headers (DevTools → Network) – you should see Vercel-Cache-Tag: article-<id>.
  4. Update the article in Strapi’s admin UI.
  5. Check the webhook logs (/admin/settings/webhooks → “History”) – a successful 200 response indicates the handler ran.
  6. Refresh the page – the CDN should serve a fresh response (you’ll see a new x-vercel-cache header value or a MISS).

If the cache tag is not being invalidated, verify:

  • The Authorization header matches.
  • The Vercel token has the Edge Cache: Invalidate scope.
  • The tag string exactly matches the one sent by Next.js (case‑sensitive).

6. Common pitfalls and troubleshooting

SymptomLikely causeFix
401 Unauthorized from webhook handlerToken mismatchEnsure WEBHOOK_TOKEN is identical in Strapi config and Vercel env.
No Vercel-Cache-Tag header in responsecacheTag not called before any async fetchPlace cacheTag at the top of the server component/function.
Cache not purged, still HIT after updateTag typo or missing in invalidate requestLog the tag variable before calling Vercel SDK; compare with header value.
Vercel SDK throws Invalid projectWrong projectIdOrNameUse the exact project slug as shown in Vercel dashboard or the numeric project ID.
Webhook payload missing entry.idWebhook configured for a collection type without id exposureIn Strapi webhook settings, enable “Include full entry data”.

7. Full example repository structure

code
my-next-app/
├─ app/
│  ├─ api/
│  │  └─ strapi-revalidate/
│  │     └─ route.ts   ← webhook handler
│  └─ articles/[slug]/
│     └─ page.tsx      ← cacheTag usage
├─ .env.local
│  ├─ WEBHOOK_TOKEN=…
│  ├─ VERCEL_BEARER_TOKEN=…
│  └─ VERCEL_PROJECT=my-next-app
└─ package.json

Deploy with vercel or via the Vercel dashboard; the environment variables are automatically injected.


8. When to prefer Vercel tag invalidation over ISR revalidation

Use caseVercel tag invalidationNext.js revalidateTag
Immediate purge after any content change✅ Immediate, works for any cache‑able response❌ Only works from within the same deployment
Invalidation across multiple deployments (preview → production)✅ Tags are shared across deployments❌ Tags are scoped to a single deployment
Granular control per content version✅ Custom tags (article‑123‑v5)✅ Same, but requires extra code
Simplicity – no extra SDK❌ Requires Vercel SDK or raw fetch✅ Built‑in function

If you already use Vercel’s edge‑cache for static assets or have many preview deployments, tag‑based invalidation is the most robust choice.


green and brown bamboo sticks
Photo by Ryutaro Uozumi on Unsplash

9. Summary

  • Assign cache tags in Next.js with cacheTag.
  • Configure a Strapi 5 webhook that fires on entry.update and entry.publish.
  • Build a secure API route in Next.js that verifies the bearer token and calls Vercel’s edge‑cache/invalidate‑by‑tags endpoint.
  • Optionally enrich tags with Revisionary version IDs for fine‑grained control.
  • Test the flow, watch for common pitfalls, and enjoy instant cache freshness without full redeploys.

By wiring Strapi and Vercel together, you get the performance of static generation with the editorial agility of a headless CMS.


Further reading


Happy coding!

TagsStrapiNext.jsVercelWebhookCache InvalidationISR

Sources

  1. Strapi webhook documentation
  2. Vercel Edge Cache Invalidate‑by‑Tags API
  3. Next.js cacheTag function reference
  4. Strapi blog: Trigger deployments using Vercel deploy hook and Strapi webhook
  5. Vercel SDK edgeCache.invalidateByTags example

The Revisionary team

Developers who build Revisionary, a version history plugin for Strapi 5. We write about Strapi, web development, AI and application security.

This article was written with the help of AI.

All articles