Master WordPress REST API cache invalidation using a dependency graph. Learn to track resource relationships for granular purging and improved headless performance.
When you move to a headless architecture, the biggest headache isn't fetching data—it's knowing exactly when to kill the old data. I recently spent about three weeks refactoring a high-traffic headless project that relied on broad, time-based cache expiration, which led to users seeing stale content for minutes after an update.
The solution isn't just better WordPress performance through granular Redis object cache tagging; it’s building a dependency graph that maps how your entities relate.
Initially, we tried a simple approach: whenever a post updated, we nuked the entire REST API cache for that content type. It worked, but our cache hit ratio dropped from 98% to about 65% during peak hours. We were suffering from cache stampedes because we were invalidating too much, too often. As I covered in my guide on preventing cache stampedes with request coalescing, you want to keep your origin server as quiet as possible.
We needed a way to say, "Update this post, and only invalidate the specific REST API endpoints that include this post's ID."
A dependency graph tracks the relationship between your API resources. For example, a product might depend on a category and several attributes. If the category name changes, the product API response is now stale.
To implement this, I created a simple metadata map. Every time a REST API request is generated, I hook into the response filter to record the dependencies used to build that object.
PHP#6A9955">// Example: Tracking dependencies during a REST API request add_filter('rest_post_dispatch', function($response, $server, $request) { $data = $response->get_data(); $dependencies = $response->get_header('X-WP-Cache-Dependencies'); if ($dependencies) { #6A9955">// Store the mapping in Redis: #6A9955">// Hash key: 'dependency:post_123' -> Value: ['api_endpoint_products_456', 'api_endpoint_products_789'] update_dependency_map($dependencies, $request->get_route()); } return $response; }, 10, 3);
Once you have this map, your Cache Invalidation logic becomes surgical. Instead of clearing everything, you look up the keys in your graph and purge only those specific entries.
Here is the basic logic for a purge service:
save_post or edited_{taxonomy}.DEL command in Redis to remove only the affected REST API cache keys.This approach significantly improves Headless Performance because the frontend always receives fresh data without forcing a full rebuild of the cache layer.
Before we went down this path, we experimented with a "versioning" strategy where we appended a timestamp to the URL (e.g., /products/123?v=1628394). It broke our CDN caching, which treated every version as a unique object, effectively disabling our edge-level optimizations.
The dependency graph approach keeps the URL clean while ensuring the cache stays consistent. It’s definitely more complex to maintain—you have to be disciplined about registering dependencies—but the payoff in stability is worth it.
Does this add latency to the write process?
Yes, slightly. Writing to the dependency graph happens on the save_post action, which is server-side. However, by using Redis for the graph storage, the lookup time is typically sub-millisecond, which is negligible compared to the time saved by avoiding unnecessary cache regenerations.
How do you handle complex nested dependencies? Keep your graph depth shallow. If a resource depends on something that depends on something else, try to flatten the relationship. I usually limit the graph to one level of depth to prevent recursive lookup loops.
What happens if the cache is cleared manually?
Your dependency graph will be out of sync. You’ll need a flush_cache hook that clears both the object cache and the dependency graph keys in Redis to ensure they stay in lockstep.
I’m still experimenting with how to automate the dependency tracking. Right now, it’s semi-manual, which leaves room for human error. If I were doing this again, I’d look into using an automated interceptor that watches database queries during the REST API lifecycle, though that’s much heavier on the CPU. It's a trade-off between architectural purity and raw performance.
WordPress performance hinges on reducing cold-start latency. Learn to implement request prefetching middleware to hydrate REST API responses for headless apps.
Read moreMaster WordPress performance with sharded multi-level caching. Learn to implement tiered TTL strategies to scale headless environments under high-concurrency.