Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
Wordpress

WordPress: Fix a REST API Endpoint That Keeps Returning Cached Data After You Update a Post

- 27.09.26 - ErcanOPAK

📝 The REST Endpoint That Never Heard About the Update

A caching plugin or a reverse proxy in front of WordPress typically hooks into post-save actions to purge the cached HTML of the pages that display a post – but the WordPress REST API serves its own, separate set of URLs (/wp-json/wp/v2/posts/123 and similar), which a cache purge rule written with only the theme’s page templates in mind can miss entirely. The post itself updates correctly in the database, the website’s normal pages show the new content immediately, and the REST API – queried directly by a headless frontend or a mobile app – keeps serving the stale cached version until its own cache separately expires.

🔎 The Problem

A typical page-cache purge rule, configured (or defaulted) to clear
cached URLs matching the site'"'"'s normal page patterns:

  /                         <- cleared on any post update
  /category/*               <- cleared on any post update
  /2026/*/post-slug/         <- cleared for the specific post

But a headless frontend or mobile app is actually querying:

  /wp-json/wp/v2/posts/123
  /wp-json/wp/v2/posts?slug=post-slug

Neither of these matches any pattern the purge rule was written for -
the REST endpoint keeps serving its own separately cached response
(whether cached by the same plugin, a CDN, or a reverse proxy in
front of the whole site) until that cache entry expires on its own
schedule, completely independent of the post actually being updated.

✅ Fix: Purge REST API URLs Explicitly, Not Just Theme Pages

  • Whatever cache-purge hook fires on post save (whether a plugin’s own action or a custom function hooked to save_post) needs to explicitly include the post’s REST API URL pattern in whatever it purges – most popular caching plugins support adding custom URL patterns to a purge rule, but it has to be added, since it isn’t assumed by default.
  • For a CDN or reverse proxy sitting in front of WordPress with its own independent cache, a purge API call (Cloudflare’s cache-purge API, or an equivalent) triggered from the same save_post hook keeps that outer cache layer in sync too – a purge rule that only clears WordPress’s own internal cache does nothing about a completely separate CDN cache layer sitting in front of it.
  • Setting a deliberately short cache TTL specifically on wp-json/ routes (rather than matching the TTL used for regular page HTML) limits how stale the REST API can ever get even if a specific purge rule is missed, trading a small amount of cache efficiency for freshness on the endpoints an external app actually depends on.

⚠️ Why This Slips Past Normal Testing

  • Whoever configured (or tested) the caching setup was very likely checking the website itself – the actual rendered pages – which purge correctly, so everything looks completely fine from that vantage point, with no reason to separately check a REST endpoint that the website’s own pages never call.
  • This becomes visible specifically once a separate consumer of the REST API exists (a mobile app, a decoupled frontend, an integration) – a WordPress site used purely to render its own theme’s pages, with nothing else querying wp-json/ directly, would never surface this gap at all.

A cache purge rule only clears what it was told about – and a REST endpoint nobody mentioned when that rule was written keeps quietly serving yesterday’s answer, no matter how current the actual post is.

— WordPress Admin

Related posts:

WordPress: Create Custom Post Types for Structured Content

WordPress: Create Custom Gutenberg Blocks

WordPress Becomes Slow After Adding “Just One Plugin”

Post Views: 3

Post navigation

Windows 11: Fix Widgets That Keep Reopening on Startup After You Disable Them
ASP.NET Core: Fix Output Caching That Serves the Same Response to Every User Regardless of Query String

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

October 2026
M T W T F S S
 1234
567891011
12131415161718
19202122232425
262728293031  
« Sep    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (973)
  • How to make theater mode the default for Youtube (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (939)
  • Get the First and Last Word from a String or Sentence in SQL (847)
  • How to select distinct rows in a datatable in C# (836)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (624)
  • Add Constraint to SQL Table to ensure email contains @ (590)
  • Average of all values in a column that are not zero in SQL (553)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (526)
  • Find numbers with more than two decimal places in SQL (468)

Recent Posts

  • CSS: Fix a prefers-color-scheme Media Query That Gets Silently Overridden by a Browser Extension’s Forced Dark Mode
  • Git: Fix a Merge Commit That Silently Drops a File Because Both Branches Deleted It Differently
  • HTML5: Fix a Native Lazy-Loading Image That Never Loads Because It Sits Inside a Hidden Tab Until the User Clicks It
  • The AI Prompt That Traces a Null Reference Exception Back to the Exact Line That First Produced the Null
  • The AI Prompt That Turns a Gym Membership Contract’s Fine Print Into a Plain-English List of Cancellation Steps
  • Photoshop: Fix a Color Profile Mismatch That Makes Printed Output Look Nothing Like What You Saw On Screen
  • WordPress: Fix Search Results That Return Pages From a Theme You Deactivated Months Ago
  • Visual Studio: Fix a Test Project That Builds Fine Alone but Fails to Discover Any Tests After a NuGet Restore
  • ASP.NET Core: Fix a File Upload That Times Out on Slow Connections Only Because Kestrel’s Minimum Data Rate Feature Kicked In
  • JavaScript: Fix an Array Destructuring Default Value That Silently Never Applies Because null Was Passed Instead of Undefined

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (973)
  • How to make theater mode the default for Youtube (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (939)
  • Get the First and Last Word from a String or Sentence in SQL (847)
  • How to select distinct rows in a datatable in C# (836)

Recent Posts

  • CSS: Fix a prefers-color-scheme Media Query That Gets Silently Overridden by a Browser Extension’s Forced Dark Mode
  • Git: Fix a Merge Commit That Silently Drops a File Because Both Branches Deleted It Differently
  • HTML5: Fix a Native Lazy-Loading Image That Never Loads Because It Sits Inside a Hidden Tab Until the User Clicks It
  • The AI Prompt That Traces a Null Reference Exception Back to the Exact Line That First Produced the Null
  • The AI Prompt That Turns a Gym Membership Contract’s Fine Print Into a Plain-English List of Cancellation Steps

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com