This guide explains how to configure Sanity webhooks to automatically invalidate Netlify's cache when content is published, ensuring users see fresh content immediately.
┌─────────────────────────────────────────────────────────────┐
│ Content Flow with Webhook Cache Invalidation │
└─────────────────────────────────────────────────────────────┘
1. Content Editor publishes in Sanity
↓
2. Sanity triggers webhook to your API endpoint
↓
3. Webhook validates signature for security
↓
4. Netlify cache is purged for affected pages
↓
5. Next visitor triggers fresh SSR
↓
6. Fresh SSR fetches latest data from Sanity via TanStack Query
↓
7. User sees updated content immediately (0-2 second delay)
Without webhooks: Users might wait 5-30 minutes (depending on staleTime) before seeing updates.
With webhooks: Cache is invalidated instantly, fresh content visible on next page load.
- Sanity Studio project with webhook support
- Netlify site deployed with your TanStack Start app
- Access to both Sanity and Netlify admin panels
Generate a strong random secret for webhook authentication:
# Option 1: Use openssl
openssl rand -hex 32
# Option 2: Use Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Option 3: Online generator
# Visit: https://www.random.org/strings/Save this secret - you'll need it for both Sanity and your environment variables.
- Go to Netlify Dashboard
- Select your site
- Go to Site settings → General
- Find Site details → Site ID
- Copy the Site ID (e.g.,
abc123-def456-ghi789)
- Go to Netlify User Settings
- Click Personal access tokens
- Click New access token
- Name it:
Sanity Webhook - [Your Site Name] - Click Generate token
- IMPORTANT: Copy the token immediately - you won't be able to see it again!
- Store it securely
Add these variables to your deployment environment:
Create or update apps/web/.env.local:
# Sanity Webhook Configuration
SANITY_WEBHOOK_SECRET="your-webhook-secret-from-step-1"
# Netlify API Credentials
NETLIFY_AUTH_TOKEN="your-netlify-personal-access-token"
NETLIFY_SITE_ID="your-site-id"- Go to your Netlify site dashboard
- Navigate to Site settings → Environment variables
- Add the following variables:
| Variable Name | Value | Notes |
|---|---|---|
SANITY_WEBHOOK_SECRET |
Your generated secret | Same secret used in Sanity |
NETLIFY_AUTH_TOKEN |
Your personal access token | Keep this secure! |
NETLIFY_SITE_ID |
Your site ID | Found in Site settings |
- Click Save
- Important: Trigger a new deploy to apply the environment variables
- Go to Sanity Manage
- Select your project
- Navigate to API → Webhooks
- Click Create webhook
Fill in the following details:
Name: Netlify Cache Invalidation
URL:
https://your-domain.netlify.app/api/webhooks/sanity
Replace your-domain with your actual Netlify domain.
Dataset: production (or your target dataset)
Trigger on:
- ✅ Create
- ✅ Update
- ✅ Delete
Filter (Optional): Add a GROQ filter to only trigger for specific document types:
_type in ["event", "mediaImage", "homePage", "partner", "quote"]
Secret: Paste the webhook secret you generated in Step 1
HTTP method: POST
HTTP Headers: (Leave default)
API version: Use latest available
- Click Save
Visit your webhook endpoint directly:
GET https://your-domain.netlify.app/api/webhooks/sanity
You should see:
{
"service": "Sanity Webhook Handler",
"status": "active",
"configured": true
}If configured: false, check your environment variables.
-
Go to your Sanity Studio
-
Edit any document (e.g., update an event title)
-
Click Publish
-
Check your Netlify Function Logs:
- Go to Netlify Dashboard → Functions → sanity
- You should see:
[Sanity Webhook] Received event for event: - Followed by:
[Sanity Webhook] Successfully purged cache for tags: ["events", "homepage"]
-
Visit your website and verify the change appears immediately
- Go to API → Webhooks
- Click on your webhook
- View Recent deliveries to see:
- Delivery status (success/failure)
- Response codes
- Request/response payloads
- Go to Functions
- Select the
sanityfunction - View logs for webhook processing details
Problem: Invalid signature or missing secret
Solutions:
- Verify
SANITY_WEBHOOK_SECRETmatches in both Sanity and your environment - Ensure the environment variable is deployed (trigger new build)
- Check Netlify function logs for exact error message
Problem: Missing Netlify credentials
Solutions:
- Verify
NETLIFY_AUTH_TOKENis set correctly - Verify
NETLIFY_SITE_IDis set correctly - Check the GET endpoint shows
configured: true
Problem: Webhook succeeds but cache doesn't clear
Solutions:
- Check Netlify function logs for "Successfully purged cache" message
- Verify your Netlify token has the correct permissions
- Try manually triggering a deploy to clear all caches
- Check if you're testing in a browser with aggressive caching (try incognito mode)
Problem: Content changes but webhook never fires
Solutions:
- Check Recent deliveries in Sanity webhook settings
- Verify the webhook filter includes your document type
- Ensure you're publishing (not just saving draft)
- Check that the webhook is enabled
The webhook intelligently invalidates different caches based on content type:
| Content Type | Pages Invalidated | Reason |
|---|---|---|
event |
Events list, Event detail, Homepage | Events appear on multiple pages |
mediaImage |
Media gallery, Homepage | Images shown in gallery and homepage |
homePage |
Homepage only | Direct homepage content |
partner |
Homepage | Partners displayed on homepage |
quote |
Homepage | Quotes displayed on homepage |
| Unknown types | All pages | Safety fallback |
Before Webhooks:
- Cache invalidation: Based on
staleTime(5-30 minutes) - User sees updates: After staleTime expires
- Unnecessary API calls: Periodic refetching
After Webhooks:
- Cache invalidation: Instant (< 2 seconds)
- User sees updates: Next page load after publish
- API efficiency: Only fetch when content actually changes
- Keep webhook secret secure: Never commit to version control
- Rotate secrets periodically: Update both Sanity and environment variables
- Use HTTPS only: Webhook endpoint must be HTTPS (Netlify provides this)
- Monitor failed attempts: Check Sanity delivery logs for suspicious activity
- Limit webhook scope: Use GROQ filter to only trigger for necessary document types
Currently, the webhook purges the entire Netlify cache. For more granular control, you could:
- Implement cache tags in Netlify headers (if/when Netlify supports this)
- Track individual page caches and invalidate specific URLs
- Use Netlify's On-Demand Builders for static page regeneration
These approaches require additional implementation and aren't necessary for most sites.
- Review webhook delivery logs for failures
- Verify webhook secret hasn't been exposed
- Check cache invalidation is working as expected
- Monitor API call volume to Sanity
If you change the webhook endpoint:
- Update URL in Sanity webhook settings
- Verify new endpoint works before disabling old one
If rotating secrets:
- Generate new secret
- Update in environment variables
- Trigger new deploy
- Update in Sanity webhook settings (in that order)
- Sanity Webhooks Docs: https://www.sanity.io/docs/webhooks
- Netlify API Docs: https://docs.netlify.com/api/get-started/
- TanStack Query SSR: https://tanstack.com/query/latest/docs/framework/react/guides/ssr
✅ Completed Setup Checklist:
- Generated webhook secret
- Retrieved Netlify Site ID
- Created Netlify Personal Access Token
- Added environment variables to Netlify
- Configured webhook in Sanity Studio
- Tested webhook endpoint (GET request)
- Tested with real content publish
- Verified cache invalidation in logs
- Confirmed changes appear immediately on site
Once all steps are complete, your site will automatically show fresh content within seconds of publishing in Sanity!