Over recent months, we’ve been focused on improving WooCommerce’s runtime performance. While edge caching and HTTP cache control headers have been explored, implementing caching for admin or public-facing pages remains challenging due to per-user data variability, numerous extensibility points, and interactions with page caching plugins.
However, there’s one area that’s deterministic and important enough to benefit from built-in caching: REST API endpoints.
WooCommerce 10.5 will introduce an experimental caching engine for REST API endpoints, delivering significant performance improvements for headless stores, mobile apps, and API-heavy integrations. We’re calling on developers to test this feature and share feedback.
Introducing the RestApiCache trait
The RestApiCache trait provides an opt-in, powerful mechanism for caching responses from REST API endpoints registered with register_rest_route.
Testing with ~20,000 products (mixed simple and variable) showed:
- Cache miss: 2-2.5s response time
- Cache hit: 0.6-0.8s response time
- ~60-70% reduction in response time with cached responses
304 responses show similar performance for small payloads, but provide significant benefits for larger responses on slower networks and enable clients to reuse processed data.
Basic implementation
Here’s the simplest usage:
// Before:
class MyController {
public function register_routes() {
register_rest_route(
$this->namespace,
'/foobar',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' )
)
);
}
// After:
class MyController {
use RestApiCache;
protected function get_default_response_entity_type(): ?string {
return 'foobar';
}
public function register_routes() {
register_rest_route(
$this->namespace,
'/foobar',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => $this->with_cache( array( $this, 'get_item' ) ) //<-- Watch here!
)
);
}What you get
Depending on configuration, endpoints wrapped with with_cache() receive:
- Object cache-based response caching: Subsequent requests to the same endpoint return cached versions, reducing response times. Requires a persistent object cache.
- Cache control headers: Responses include proper
Cache-Control,Date, andETagheaders. Requests with matchingIf-None-Matchheaders return standard304 Not Modifiedresponses.
The basic example above uses sensible defaults: requests are uniquely identified by route, query string parameters, and current user; cached responses expire after one hour; and the cache automatically invalidates when related entities (like products) are updated.
By default, all the endpoints wrapped in with_cache will make use of the caching engine provided by the trait. However there are a couple of “escape hatches” that allow to disable caching (so the request will proceed as if the endpoint wasn’t wrapped in with_cache) for specific instances of endpoint hits:
- If the request includes
_skip_cache=1in the query string. For one-off cases when a noncached response is needed. - If the
woocommerce_rest_api_enable_response_cachingfilter returnsfalse(the input isfalseif_skip_cache=1, ortrueotherwise).
Note that these are relevant only when caching is enabled via configuration by a site admin (caching never happens when it’s disabled).
Configuration for more complex cases is achieved via a combination of overriding protected methods in the trait and passing a parameters array to with_cache. The final cache key can also be filtered via woocommerce_rest_api_cache_key_info.
Handling headers
Generated Cache-Control headers include:
- Visibility:
privatewhen varying by logged-in user (browser caching only),publicotherwise (CDN caching allowed) - must-revalidate: Stale responses require revalidation
- max-age: Set to cache lifetime in seconds
The Date header reflects when the response was cached, enabling accurate staleness detection.
By default, most headers are cached except:
X-WC-Cache(generated by the trait)Set-Cookie,Date,Expires,Last-Modified,AgeETag,Cache-Control,Pragma
You can customize which headers are cached via a few filters:
get_response_headers_to_include_in_caching()(inclusion list)get_response_headers_to_exclude_from_caching()(exclusion list)woocommerce_rest_api_cached_headersfilter
By default the value for the ETag header included in responses will be generated as a hash of the full response data. However this can be customized by overriding the get_data_for_etag trait method.
Configuration and setup
REST API caching is experimental and disabled by default. Enable it at WooCommerce → Settings → Advanced → Features:

Once enabled, configure in WooCommerce → Settings → Advanced → REST API Caching:
- Backend caching: Requires persistent object cache (Redis, Memcached, etc.)
- Cache control headers: Works without object cache

What’s cached in WooCommerce 10.5
The following product endpoints support caching:
/v2/products
/v2/products/<id>
/v2/products/<id>/variations
/v2/products/<id>/variations/<variation id>
/v3/products
/v3/products/<id>
/v3/products/<id>/variations
/v3/products/<id>/variations/<variation id>
/v3/products/suggested-products
/v4/products
/v4/products/<id>
/v4/products/suggested-productsFuture development may include:
- Extending caching to additional endpoint types beyond products
- Creating version string invalidators for other entity types
- Refining the trait based on real-world testing
- Potentially moving the trait to public API for extension use
Testing the feature
We invite your support in testing this experimental feature:
- Enable the feature in WooCommerce 10.5+ via
Settings → Advanced → Features - Configure caching in
Settings → Advanced → REST API Caching - Test with your integrations, especially:
- Headless storefronts
- Mobile applications
- Third-party integrations using WooCommerce REST API
- Custom REST API endpoints using the trait
- Monitor performance and cache behavior
- Report issues on GitHub
Here are a few considerations while testing:
- The behavior of the trait is highly customizable via hooks. See the code of the
RestApiCachetrait for details on the available hooks and how to use them. - Force cache misses by adding dummy query parameters:
?foo=bar - Test 304 responses with
If-None-Matchheader containing an ETag value - Verify cache invalidation when products are updated
- Monitor object cache usage and performance
Important notes
⚠️ This is an experimental feature. Breaking changes (including the behavior of the hooks fired by the trait) are possible in future iterations as we refine based on feedback.
⚠️ The RestApiCache trait is currently in the Internal namespace and intended for WooCommerce core use. We’re evaluating promotion to public API after thorough testing and refinement.
⚠️ Some web servers (particularly nginx configurations) may modify headers:
- Date header: May be overridden with send time. Check
X-WC-Datefirst. - If-None-Match header: May be stripped. The following snippet will restore it:
add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) {
$custom_header = $request->get_header( 'X-WC-If-None-Match' );
if ( $custom_header ) {
$request->set_header( 'If-None-Match', $custom_header );
}
return $result;
}, 0, 3 );Share your feedback
This feature represents a significant performance opportunity for headless WooCommerce implementations and API-heavy integrations. Your testing and feedback will help us refine this feature for broader release.
Drop your experiences, questions, and suggestions in the open discussion on GitHub.
Leave a Reply