[{"data":1,"prerenderedAt":743},["ShallowReactive",2],{"post-\u002Fhow-to-revalidate-next-js-isr-cache-on-demand-cache-regeneration":3},{"page":4,"translation":620,"nav":622,"related":735,"random":736},{"id":5,"title":6,"body":7,"categories":605,"category":607,"date":608,"description":609,"draft":610,"extension":611,"image":612,"kind":607,"lang":613,"meta":614,"navigation":238,"path":615,"readingTime":248,"seo":616,"slug":617,"stem":617,"tags":607,"translationKey":617,"type":606,"updated":618,"__hash__":619},"posts\u002Fhow-to-revalidate-next-js-isr-cache-on-demand-cache-regeneration.md","How to Revalidate Next.js ISR Cache? On-Demand Cache Regeneration",{"type":8,"value":9,"toc":598},"minimark",[10,60,63,82,85,90,186,188,195,198,214,221,368,370,379,399,406,574,577,580,594],[11,12,13,21],"blockquote",{},[14,15,16,17],"p",{},"💡 ",[18,19,20],"strong",{},"Quick Summary (TL;DR):",[22,23,24,31,42],"ul",{},[25,26,27,30],"li",{},[18,28,29],{},"What is ISR?"," Incremental Static Regeneration allows Next.js sites to update static pages in the background without rebuilding the entire site.",[25,32,33,36,37,41],{},[18,34,35],{},"What is On-Demand Revalidation?"," Purging the cache instantly for a specific path via an API endpoint, rather than waiting for a predefined interval (e.g., ",[38,39,40],"code",{},"revalidate: 60",").",[25,43,44,47,48,51,52,55,56,59],{},[18,45,46],{},"Pages vs. App Router:"," Pages Router uses ",[38,49,50],{},"res.revalidate()",", while App Router (Next.js 13+) introduces route-based ",[38,53,54],{},"revalidatePath()"," and tag-based ",[38,57,58],{},"revalidateTag()"," methods.",[14,61,62],{},"Next.js allows you to create or update static pages after you’ve built your site. Incremental Static Regeneration (ISR) enables you to use static-generation on a per-page basis, without needing to rebuild the entire site. On-demand revalidation, which was introduced in version 12.1, was the primary reason I chose Next.js over Nuxt.js when building my headless WordPress setup. A few months later, Nuxt.js introduced a similar feature, bringing Nuxt (Vue) back into the game.",[14,64,65,66,69,70,73,74,77,78,81],{},"Starting with ",[38,67,68],{},"v12.1.0",", Next.js introduced experimental On-Demand Incremental Static Regeneration to manually purge the cache for a specific page. With version ",[38,71,72],{},"v12.2.0",", the feature became stable. Changing the function name from ",[38,75,76],{},"unstable_revalidate()"," to ",[38,79,80],{},"revalidate()"," was a breaking change for sites using the experimental feature. I spent an hour fixing revalidation when my site suddenly stopped regenerating the cache.",[83,84],"hr",{},[86,87,89],"h3",{"id":88},"nextjs-revalidation-methods-comparison","Next.js Revalidation Methods Comparison",[91,92,93,122],"table",{},[94,95,96],"thead",{},[97,98,99,104,111,117],"tr",{},[100,101,103],"th",{"align":102},"left","Feature \u002F Method",[100,105,106,107,110],{"align":102},"Pages Router (",[38,108,109],{},"res.revalidate",")",[100,112,113,114,110],{"align":102},"App Router (",[38,115,116],{},"revalidatePath",[100,118,113,119,110],{"align":102},[38,120,121],{},"revalidateTag",[123,124,125,144,170],"tbody",{},[97,126,127,133,139,142],{},[128,129,130],"td",{"align":102},[18,131,132],{},"Usage Location",[128,134,135,136,110],{"align":102},"API Routes (",[38,137,138],{},"pages\u002Fapi\u002F...",[128,140,141],{"align":102},"Route Handlers \u002F Server Actions",[128,143,141],{"align":102},[97,145,146,151,157,163],{},[128,147,148],{"align":102},[18,149,150],{},"Targeting",[128,152,153,154,41],{"align":102},"Path-based (e.g., ",[38,155,156],{},"\u002Fblog\u002Fpost-1",[128,158,159,160,41],{"align":102},"Path-based (static path or dynamic ",[38,161,162],{},"\u002Fblog\u002F[slug]",[128,164,165,166,169],{"align":102},"Tag-based (associated with ",[38,167,168],{},"fetch"," data tags).",[97,171,172,177,180,183],{},[128,173,174],{"align":102},[18,175,176],{},"Execution",[128,178,179],{"align":102},"Rebuilds the specific page HTML on demand.",[128,181,182],{"align":102},"Invalidates page components and cache in the background.",[128,184,185],{"align":102},"Invalidates only the fetch requests matching the tag.",[83,187],{},[189,190,192,193,110],"h2",{"id":191},"_1-classic-approach-pages-router-resrevalidate","1. Classic Approach: Pages Router (",[38,194,109],{},[14,196,197],{},"First, create a secret token known only by your Next.js application. This secret will prevent unauthorized clients from abusing the revalidation API Route. You can trigger the route (either manually, via postman, or with a headless CMS webhook) using the following URL structure:",[199,200,205],"pre",{"className":201,"code":202,"language":203,"meta":204,"style":204},"language-http shiki shiki-themes github-light github-dark","https:\u002F\u002F\u003Cyour-domain.com>\u002Fapi\u002Frevalidate?secret=MY_SECRET_TOKEN&path=\u002Fblog\u002Fmy-post\n","http","",[38,206,207],{"__ignoreMap":204},[208,209,212],"span",{"class":210,"line":211},"line",1,[208,213,202],{},[14,215,216,217,220],{},"Next, add the secret as an Environment Variable to your hosting environment (e.g., Vercel, Netlify, or your custom ",[38,218,219],{},".env"," file). Finally, create the revalidation API Route handler:",[199,222,226],{"className":223,"code":224,"language":225,"meta":204,"style":204},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F pages\u002Fapi\u002Frevalidate.js\n\nexport default async function handler(req, res) {\n  \u002F\u002F Check for secret to confirm this is a valid request\n  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {\n    return res.status(401).json({ message: 'Invalid token' });\n  }\n\n  const path = req.query.path;\n  if (!path) {\n    return res.status(400).json({ message: 'Path parameter is required' });\n  }\n\n  try {\n    \u002F\u002F This must be the actual path, not a rewritten path\n    \u002F\u002F e.g. for \"\u002Fblog\u002F[slug]\" this should be \"\u002Fblog\u002Fpost-1\"\n    await res.revalidate(path);\n    return res.json({ revalidated: true });\n  } catch (err) {\n    \u002F\u002F If there was an error, Next.js will continue\n    \u002F\u002F to show the last successfully generated page\n    return res.status(500).send('Error revalidating');\n  }\n}\n","javascript",[38,227,228,233,240,246,252,258,264,270,275,281,287,293,298,303,309,315,321,327,333,339,345,351,357,362],{"__ignoreMap":204},[208,229,230],{"class":210,"line":211},[208,231,232],{},"\u002F\u002F pages\u002Fapi\u002Frevalidate.js\n",[208,234,236],{"class":210,"line":235},2,[208,237,239],{"emptyLinePlaceholder":238},true,"\n",[208,241,243],{"class":210,"line":242},3,[208,244,245],{},"export default async function handler(req, res) {\n",[208,247,249],{"class":210,"line":248},4,[208,250,251],{},"  \u002F\u002F Check for secret to confirm this is a valid request\n",[208,253,255],{"class":210,"line":254},5,[208,256,257],{},"  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {\n",[208,259,261],{"class":210,"line":260},6,[208,262,263],{},"    return res.status(401).json({ message: 'Invalid token' });\n",[208,265,267],{"class":210,"line":266},7,[208,268,269],{},"  }\n",[208,271,273],{"class":210,"line":272},8,[208,274,239],{"emptyLinePlaceholder":238},[208,276,278],{"class":210,"line":277},9,[208,279,280],{},"  const path = req.query.path;\n",[208,282,284],{"class":210,"line":283},10,[208,285,286],{},"  if (!path) {\n",[208,288,290],{"class":210,"line":289},11,[208,291,292],{},"    return res.status(400).json({ message: 'Path parameter is required' });\n",[208,294,296],{"class":210,"line":295},12,[208,297,269],{},[208,299,301],{"class":210,"line":300},13,[208,302,239],{"emptyLinePlaceholder":238},[208,304,306],{"class":210,"line":305},14,[208,307,308],{},"  try {\n",[208,310,312],{"class":210,"line":311},15,[208,313,314],{},"    \u002F\u002F This must be the actual path, not a rewritten path\n",[208,316,318],{"class":210,"line":317},16,[208,319,320],{},"    \u002F\u002F e.g. for \"\u002Fblog\u002F[slug]\" this should be \"\u002Fblog\u002Fpost-1\"\n",[208,322,324],{"class":210,"line":323},17,[208,325,326],{},"    await res.revalidate(path);\n",[208,328,330],{"class":210,"line":329},18,[208,331,332],{},"    return res.json({ revalidated: true });\n",[208,334,336],{"class":210,"line":335},19,[208,337,338],{},"  } catch (err) {\n",[208,340,342],{"class":210,"line":341},20,[208,343,344],{},"    \u002F\u002F If there was an error, Next.js will continue\n",[208,346,348],{"class":210,"line":347},21,[208,349,350],{},"    \u002F\u002F to show the last successfully generated page\n",[208,352,354],{"class":210,"line":353},22,[208,355,356],{},"    return res.status(500).send('Error revalidating');\n",[208,358,360],{"class":210,"line":359},23,[208,361,269],{},[208,363,365],{"class":210,"line":364},24,[208,366,367],{},"}\n",[83,369],{},[189,371,373,374,376,377,110],{"id":372},"_2-modern-approach-app-router-revalidatepath-and-revalidatetag","2. Modern Approach: App Router (",[38,375,116],{}," and ",[38,378,121],{},[14,380,381,382,385,386,389,390,393,394,389,396,398],{},"Starting with Next.js 13 and later versions (Next.js 14, 15+), route management and data caching are handled within the ",[18,383,384],{},"App Router"," architecture. Instead of the legacy Pages Router API response methods, you should use ",[18,387,388],{},"Route Handlers"," or ",[18,391,392],{},"Server Actions"," alongside ",[38,395,116],{},[38,397,121],{}," for fine-grained cache purging.",[14,400,401,402,405],{},"Here is an example of a dynamic Route Handler (",[38,403,404],{},"app\u002Fapi\u002Frevalidate\u002Froute.js","):",[199,407,409],{"className":223,"code":408,"language":225,"meta":204,"style":204},"\u002F\u002F app\u002Fapi\u002Frevalidate\u002Froute.js\nimport { revalidatePath, revalidateTag } from 'next\u002Fcache';\n\nexport async function POST(request) {\n  const { searchParams } = new URL(request.url);\n  const secret = searchParams.get('secret');\n  const path = searchParams.get('path');\n  const tag = searchParams.get('tag');\n\n  \u002F\u002F Verify the secret token\n  if (secret !== process.env.MY_SECRET_TOKEN) {\n    return Response.json({ message: 'Invalid token' }, { status: 401 });\n  }\n\n  try {\n    if (path) {\n      \u002F\u002F Purge cache for a specific path (e.g., \u002Fblog\u002Fmy-post)\n      revalidatePath(path);\n      return Response.json({ revalidated: true, path });\n    }\n\n    if (tag) {\n      \u002F\u002F Purge cache for a specific data tag (e.g., 'posts')\n      \u002F\u002F Affects fetch requests initiated with { next: { tags: ['posts'] } }\n      revalidateTag(tag);\n      return Response.json({ revalidated: true, tag });\n    }\n\n    return Response.json({ message: 'Missing path or tag parameter' }, { status: 400 });\n  } catch (err) {\n    return Response.json({ message: 'Failed to revalidate' }, { status: 500 });\n  }\n}\n",[38,410,411,416,421,425,430,435,440,445,450,454,459,464,469,473,477,481,486,491,496,501,506,510,515,520,525,531,537,542,547,553,558,564,569],{"__ignoreMap":204},[208,412,413],{"class":210,"line":211},[208,414,415],{},"\u002F\u002F app\u002Fapi\u002Frevalidate\u002Froute.js\n",[208,417,418],{"class":210,"line":235},[208,419,420],{},"import { revalidatePath, revalidateTag } from 'next\u002Fcache';\n",[208,422,423],{"class":210,"line":242},[208,424,239],{"emptyLinePlaceholder":238},[208,426,427],{"class":210,"line":248},[208,428,429],{},"export async function POST(request) {\n",[208,431,432],{"class":210,"line":254},[208,433,434],{},"  const { searchParams } = new URL(request.url);\n",[208,436,437],{"class":210,"line":260},[208,438,439],{},"  const secret = searchParams.get('secret');\n",[208,441,442],{"class":210,"line":266},[208,443,444],{},"  const path = searchParams.get('path');\n",[208,446,447],{"class":210,"line":272},[208,448,449],{},"  const tag = searchParams.get('tag');\n",[208,451,452],{"class":210,"line":277},[208,453,239],{"emptyLinePlaceholder":238},[208,455,456],{"class":210,"line":283},[208,457,458],{},"  \u002F\u002F Verify the secret token\n",[208,460,461],{"class":210,"line":289},[208,462,463],{},"  if (secret !== process.env.MY_SECRET_TOKEN) {\n",[208,465,466],{"class":210,"line":295},[208,467,468],{},"    return Response.json({ message: 'Invalid token' }, { status: 401 });\n",[208,470,471],{"class":210,"line":300},[208,472,269],{},[208,474,475],{"class":210,"line":305},[208,476,239],{"emptyLinePlaceholder":238},[208,478,479],{"class":210,"line":311},[208,480,308],{},[208,482,483],{"class":210,"line":317},[208,484,485],{},"    if (path) {\n",[208,487,488],{"class":210,"line":323},[208,489,490],{},"      \u002F\u002F Purge cache for a specific path (e.g., \u002Fblog\u002Fmy-post)\n",[208,492,493],{"class":210,"line":329},[208,494,495],{},"      revalidatePath(path);\n",[208,497,498],{"class":210,"line":335},[208,499,500],{},"      return Response.json({ revalidated: true, path });\n",[208,502,503],{"class":210,"line":341},[208,504,505],{},"    }\n",[208,507,508],{"class":210,"line":347},[208,509,239],{"emptyLinePlaceholder":238},[208,511,512],{"class":210,"line":353},[208,513,514],{},"    if (tag) {\n",[208,516,517],{"class":210,"line":359},[208,518,519],{},"      \u002F\u002F Purge cache for a specific data tag (e.g., 'posts')\n",[208,521,522],{"class":210,"line":364},[208,523,524],{},"      \u002F\u002F Affects fetch requests initiated with { next: { tags: ['posts'] } }\n",[208,526,528],{"class":210,"line":527},25,[208,529,530],{},"      revalidateTag(tag);\n",[208,532,534],{"class":210,"line":533},26,[208,535,536],{},"      return Response.json({ revalidated: true, tag });\n",[208,538,540],{"class":210,"line":539},27,[208,541,505],{},[208,543,545],{"class":210,"line":544},28,[208,546,239],{"emptyLinePlaceholder":238},[208,548,550],{"class":210,"line":549},29,[208,551,552],{},"    return Response.json({ message: 'Missing path or tag parameter' }, { status: 400 });\n",[208,554,556],{"class":210,"line":555},30,[208,557,338],{},[208,559,561],{"class":210,"line":560},31,[208,562,563],{},"    return Response.json({ message: 'Failed to revalidate' }, { status: 500 });\n",[208,565,567],{"class":210,"line":566},32,[208,568,269],{},[208,570,572],{"class":210,"line":571},33,[208,573,367],{},[14,575,576],{},"Now you have a dynamic API endpoint to refresh your Next.js cache. The way you trigger this endpoint depends on your backend architecture. Typically, you make a GET or POST request to this endpoint passing the path to be revalidated along with your secret token.",[14,578,579],{},"For my headless WordPress setup, I created a custom plugin that triggers this Next.js endpoint whenever I add, edit, or update a post, page, or category.",[14,581,582,583,593],{},"For detailed information, please refer to the official ",[584,585,592],"a",{"href":586,"rel":587,"target":591},"https:\u002F\u002Fnextjs.org\u002Fdocs\u002Fapp\u002Fbuilding-your-application\u002Fdata-fetching\u002Fincremental-static-regeneration",[588,589,590],"nofollow","noopener","noreferrer","_blank","Next.js Incremental Static Regeneration documentation",".",[595,596,597],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":204,"searchDepth":235,"depth":235,"links":599},[600,601,603],{"id":88,"depth":242,"text":89},{"id":191,"depth":235,"text":602},"1. Classic Approach: Pages Router (res.revalidate)",{"id":372,"depth":235,"text":604},"2. Modern Approach: App Router (revalidatePath and revalidateTag)",[606],"technical",null,"2022-11-10","Learn how to dynamically trigger Incremental Static Regeneration (ISR) in Next.js using on-demand revalidation for both Pages Router and App Router.",false,"md","\u002Fimages\u002Fhero\u002Fcache-regeneration.avif","en",{},"\u002Fhow-to-revalidate-next-js-isr-cache-on-demand-cache-regeneration",{"title":6,"description":609},"how-to-revalidate-next-js-isr-cache-on-demand-cache-regeneration","2026-06-21","hlpyU_DxsuWJk0UHhUHeJXx4FyYUipNEBCIWOyd_AXk",{"path":621},"\u002Ftr\u002Fnext-js-on-bellegi-nasil-yenilenir-on-demand-cache-regeneration",{"prev":623,"next":626,"others":629,"lucky":734,"readingTime":248},{"path":624,"title":625},"\u002Fthe-top-php-certification-programs-for-developers","The Top PHP Certification Programs for Developers",{"path":627,"title":628},"\u002Ftips-for-translating-a-wordpress-plugin-wordpress-theme-to-turkish","Tips for Translating a WordPress Plugin \u002F WordPress Theme to Turkish",[630,633,636,639,642,645,648,651,654,657,660,663,666,669,672,675,678,681,684,687,690,693,696,699,702,705,708,709,710,713,716,719,722,725,728,731],{"path":631,"title":632},"\u002Ffull-stack-project-development","Sample REST API Project",{"path":634,"title":635},"\u002Frest-api-authentication","How to Perform REST API Authentication?",{"path":637,"title":638},"\u002Frest-api-design","REST API Design: Principles and Output Format",{"path":640,"title":641},"\u002Frest-api-documentation-and-testing","How to Document and Test a REST API?",{"path":643,"title":644},"\u002Frest-api-error-handling","How to Perform REST API Error Handling?",{"path":646,"title":647},"\u002Frest-api-security","How to Secure a REST API?",{"path":649,"title":650},"\u002Frest-api-uri-structure","What Should the REST API URI Structure Be?",{"path":652,"title":653},"\u002Ftroubleshooting-cyberpanel-inaccessibility-after-ubuntu-release-upgrade","Troubleshooting CyberPanel Inaccessibility After Ubuntu Release Upgrade",{"path":655,"title":656},"\u002Freset-wordpress-admin-password-using-wp-cli","Reset WordPress Admin Password Using WP-CLI",{"path":658,"title":659},"\u002Fgraphql-vs-rest-api-which-is-the-best-choice-for-headless-wordpress","GraphQL vs REST API: Which is the Best Choice for Headless WordPress?",{"path":661,"title":662},"\u002Fgrow-your-business-in-turkey-with-expert-wordpress-plugin-and-theme-localization-and-support-services","Grow Your Business in Turkey with Expert WordPress Plugin and Theme Localization and Support Services",{"path":664,"title":665},"\u002Fgetting-started-with-devops-understanding-the-principles-and-adopting-the-tools","Getting Started with DevOps: Understanding the Principles and Adopting the Tools",{"path":667,"title":668},"\u002Fphp-graphql-development-advanced-techniques-for-optimizing-your-apis","PHP GraphQL Development: Advanced Techniques for Optimizing Your APIs",{"path":670,"title":671},"\u002Fadvanced-techniques-for-dependency-injection-in-php-tips-code-samples-and-faqs","Advanced Techniques for Dependency Injection in PHP: Tips, Code Samples, and FAQs",{"path":673,"title":674},"\u002Fmaximize-the-potential-of-headless-wordpress-with-graphql","Maximize the Potential of Headless WordPress with GraphQL",{"path":676,"title":677},"\u002Fwriting-clean-modular-and-reusable-code-in-php","Best Practices for Writing Clean, Modular, and Reusable Code in PHP",{"path":679,"title":680},"\u002Fheadless-cmss-an-overview-of-popular-alternatives-to-contentful-and-wordpress","Headless CMSs: An Overview of Popular Alternatives to Contentful and WordPress",{"path":682,"title":683},"\u002Fci-cd-for-php-a-comprehensive-guide","CI\u002FCD for PHP: A Comprehensive Guide",{"path":685,"title":686},"\u002Fintroduction-to-php-namespaces-a-beginners-guide-to-structuring-your-code","Introduction to PHP Namespaces: A Beginner's Guide to Structuring Your Code",{"path":688,"title":689},"\u002Fwhat-is-graylog-a-powerful-tool-for-collecting-indexing-and-analyzing-log-data","What is Graylog? A Powerful Tool for Collecting, Indexing, and Analyzing Log Data",{"path":691,"title":692},"\u002Felevate-your-turkish-audience-experience-with-professional-wordpress-plugin-and-theme-translation","Elevate Your Turkish Audience Experience with Professional WordPress Plugin and Theme Translation",{"path":694,"title":695},"\u002Fhow-to-set-up-a-self-hosted-api-gateway-a-comprehensive-guide","How to Set Up a Self-Hosted API Gateway: A Comprehensive Guide",{"path":697,"title":698},"\u002Fdifference-between-generators-and-iterators-in-php","The Key Differences Between PHP Generators and Iterators",{"path":700,"title":701},"\u002Fphp-and-machine-learning-a-winning-combination-with-php-ml","PHP and Machine Learning: A Winning Combination with PHP-ML",{"path":703,"title":704},"\u002Fphp-generators-a-beginners-guide-to-iteration","PHP Generators: A Beginner's Guide to Iteration",{"path":706,"title":707},"\u002Fmastering-closures-in-javascript-a-beginners-guide","Mastering Closures in JavaScript: A Beginner's Guide",{"path":624,"title":625},{"path":627,"title":628},{"path":711,"title":712},"\u002Fall-about-headless-wordpress","All About Headless WordPress",{"path":714,"title":715},"\u002Finstall-composer-on-ubuntu","How to Install Composer on Ubuntu \u002F Linux",{"path":717,"title":718},"\u002Fwhat-is-an-api-gateway","What is an API Gateway? Should You Use It?",{"path":720,"title":721},"\u002Fis-jwt-safe-or-is-it-vulnerable","Is JWT Safe or Is It Vulnerable?",{"path":723,"title":724},"\u002Ftailwind-css-to-use-or-not-to-use-that-is-the-question","Tailwind CSS! To use? Or not to use? That is the question.",{"path":726,"title":727},"\u002Fwhat-is-hateoas","What is HATEOAS?",{"path":729,"title":730},"\u002Fhello-world","Hello World: A New Multilingual Journey",{"path":732,"title":733},"\u002Fwhat-is-ecmascript","What is ECMAScript? What is not?",{"path":670,"title":671},[],[737,739,741],{"path":634,"title":635,"date":738},"2026-06-20",{"path":714,"title":715,"date":740},"2022-05-13",{"path":664,"title":665,"date":742},"2023-01-20",1782141981932]