WordPress performance can be transformed by moving REST API logic to the edge. Learn how to use CDN workers to offload requests and slash origin latency.
When you're running a high-traffic headless WordPress site, the REST API often becomes your primary bottleneck. Last month, I spent about three days debugging a site where the TTFB was consistently hitting 800ms because every single request for post data was forcing a full PHP bootstrap and a dozen database queries. We needed a way to serve these requests without hitting the origin server for every call.
That’s when we moved to an Edge-Side Rendering (ESR) pattern using Cloudflare Workers. By intercepting requests at the CDN layer, we can serve cached JSON responses directly from the global network, bypassing the origin entirely for static or semi-static payloads.
Traditional WordPress caching hits the disk or an object cache like Redis. While fast, it still requires the server to receive the request, initialize WordPress, and verify the user or route. Edge computing changes the game by moving that logic to the CDN.
When a client hits your endpoint, the worker checks if a valid response exists in the KV (Key-Value) store. If it does, it returns the JSON immediately. If not, it fetches from the origin, caches the result, and returns it. This approach is significantly more efficient than standard WordPress Database Optimization: Implementing HyperDB for Scaling, as it avoids the database layer for the majority of your traffic.
To get this working, you don't need to overhaul your entire site. Start with your most requested endpoints, like wp/v2/posts.
Here is a simplified look at how a Cloudflare Worker handles this request:
JAVASCRIPTaddEventListener(CE9178">'fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { const cacheKey = new Request(request.url, request) const cachedResponse = await KV_STORE.get(cacheKey.url) if (cachedResponse) { return new Response(cachedResponse, { headers: { CE9178">'Content-Type': CE9178">'application/json', CE9178">'X-Edge-Cache': CE9178">'HIT' } }) } const response = await fetch(request) const json = await response.json() await KV_STORE.put(cacheKey.url, JSON.stringify(json), { expirationTtl: 3600 }) return new Response(JSON.stringify(json), { headers: { CE9178">'Content-Type': CE9178">'application/json', CE9178">'X-Edge-Cache': CE9178">'MISS' } }) }
This logic effectively offloads the REST API from your PHP environment. By setting a TTL (Time-To-Live), you ensure that your origin only sees a request once an hour, rather than once per user session.
The biggest hurdle with this architecture is cache invalidation. If you update a post in WordPress, the edge worker won't know unless you tell it. I’ve found that using a save_post hook to purge the KV store is the most reliable method.
You’ll need to trigger a purge request from your origin to the CDN’s API whenever content changes. If you're building a highly interactive app, you might also want to look into API Request Batching: Reduce Network Overhead and Latency to minimize the number of individual calls that need to be cached in the first place.
Is this a silver bullet? Not exactly. If your site relies heavily on personalized data—like user-specific settings or real-time cart contents—you cannot cache those responses at the edge. We initially tried to cache everything, which led to some confusing user-state bugs where one person's cart was visible to another.
We had to implement a strict exclusion list in our worker logic to skip caching for any request containing a nonce or authentication header. This is why mastering WordPress REST API Middleware: Implementing JWT Scoped Authorization is so vital; you need to know exactly which routes require authentication so you can safely bypass the edge cache for those requests.
Does this replace server-side caching? No. It complements it. Use Object Caching (Redis/Memcached) for internal processes and Edge-Side Rendering for public-facing API responses.
How do I handle dynamic data? Exclude authenticated routes or specific API paths from your worker logic. Only cache public, GET-based data.
What happens if the CDN is down? Your worker logic should include a "fail-open" pattern where it falls back to the origin server if the KV store is unreachable or the fetch fails.
I’m still experimenting with how to better handle partial cache invalidation. Currently, a single post update purges the entire endpoint cache, which is overkill for a site with thousands of posts. Next time, I’ll likely implement a tag-based purging system to keep the cache warmer for longer.
Master WordPress performance monitoring using OpenTelemetry. Learn how to implement distributed tracing for your REST API to find and fix hidden bottlenecks.
Read moreWordPress performance hinges on minimizing MySQL write-latency. Learn to decouple your REST API mutations using asynchronous queues for faster, scalable writes.