If you have CoCart Plus v1.4 or higher, and have already enabled the rate limiter from CoCart, then it will take precedence. Otherwise your free to use the Rate Limiter from API Security.
Popular sites can become the targets of malicious actors. One example of known abusive patterns is making many requests in a very short timeframe to try to overwhelm the site.
What it does?
Rate limiting is to prevent abuse on endpoints from excessive calls and performance degradation on the machine running the store.
Limiting is based on user ID for registered users (logged in) and IP address for guest users (unauthenticated requests).
It also offers standard support for running behind a proxy, load balancer, etc. This is also optional and is disabled by default.
The rate limiting uses a wp_api_rate_limits
table for tracking the request count in any given request window.
Limit information
By default a maximum of 25 requests can be made within a 10-second time frame. The limit will be reset once the timeframe has expired.
Methods restricted by Rate Limiting
GET
, POST
, PUT
, PATCH
, and DELETE
Constants
Set the following constants in your wp-config.php
for global control.
define( 'API_SECURITY_RATE_LIMITING_ENABLED', true );
if ( defined( 'API_SECURITY_RATE_LIMITING_ENABLED' ) && API_SECURITY_RATE_LIMITING_ENABLED ) {
define( 'API_SECURITY_RATE_LIMITING_PROXY_SUPPORT', false );
define( 'API_SECURITY_RATE_LIMITING_LIMIT', 25 );
define( 'API_SECURITY_RATE_LIMITING_SECONDS', 10 );
}
api_security_rate_limit_options – Filtering provides more defined control and takes precedence over any constants set in your wp-config.php
file.
add_filter( 'api_security_rate_limit_options', function() {
return [
'enabled' => true, // enables/disables Rate Limiting. Default: true
'proxy_support' => false, // enables/disables Proxy support. Default: false
'limit' => 25, // limit of request per timeframe. Default: 25
'seconds' => 10, // timeframe in seconds. Default: 10
];
} );
You can take it a step further by configuring for a specific endpoint while every other endpoint uses the same rate limit.
add_filter( 'api_security_rate_limit_options', function( $default_options ) {
// Restrict viewing posts too quickly. Helpful against bots.
if ( preg_match( '#/wp/v2/posts#', $GLOBALS['wp']->query_vars['rest_route'] ) ) {
return [
'enabled' => true,
'proxy_support' => $default_options->proxy_support,
'limit' => 3,
'seconds' => 60
];
}
// All other endpoints are rate limited to these conditions.
return [
'enabled' => true,
'proxy_support' => $default_options->proxy_support,
'limit' => 10,
'seconds' => 10
];
} );
Supporting Proxies
Like any mechanism that restricts usage to counter potential abuse of an API, this is a sensitive feature that should be used carefully.
In a scenario where your app is behind another service layer (a proxy, load balancer, etc.), the developer should enable standard proxy support through the options filter.
Otherwise rate limiting might be wrongly triggered and group-limit requests.
Proxy standard support
For the proxy_support
option to work properly, service layers (load balancer, cache service, CDNs, etc.) must be passing the originating IP supported through standard IP forwarding headers, namely:
X_REAL_IP
|CLIENT_IP
Custom popular implementations that simplify obtaining the origin IP for the requestX_FORWARDED_FOR
De-facto standard header for identifying the originating IP, DocumentationX_FORWARDED
Documentation, RFC 7239
This is disabled by default and may not be needed if you decide to provide your own fingerprint ID.
Limit usage information observability
Current limit information can be observed via custom response headers:
RateLimit-Limit
Maximum requests per time frame.RateLimit-Remaining
Requests available during current time frame.RateLimit-Retry-After
Seconds until requests are unblocked again. Only shown when the limit is reached.RateLimit-Reset
Unix timestamp of next time frame reset.
RateLimit-Limit: 5
RateLimit-Remaining: 0
RateLimit-Retry-After: 28
RateLimit-Reset: 1654880642
Tracking Abuses
Developers can use the api_security_rate_limit_exceeded
action hook to track and handle instances of API abuse.
add_action(
'api_security_rate_limit_exceeded',
function ( $offending_ip ) { /* Custom tracking implementation */ }
);
Fingerprint ID
For eCommerce, there is a common scenario of fraud checkouts and card-testing is always a problem, such as getting hit with rotating IPs.
To help account for that scenario, you can provide a different trigger called a Fingerprint. You can use your own custom fingerprinting ID system using the filter api_security_rate_limit_id
.
add_filter( 'api_security_rate_limit_id', function( $id ) {
$user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? apis_clean( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
$accept_language = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) : '';
return md5( $id . $user_agent . $accept_language );
} );
Testing Guide
Without proxy support
- In a short window, keep making API requests.
- Check that RateLimit-xxx headers change on each request.
- Check that once you’ve hit the limit, an error response is returned. You can modify the limits using the options filter to make it easier to test.
With proxy support, do the same as before and
- Enable proxy support
- Make your requests with one of the following request headers containing always the same IP address:
- X-Real-IP or Client-IP
- X-Forwarded-For
- Forwarded
User Facing Testing
- Try to accessing
/wp-json/wp/v2/posts
beyond current limits (currently 25 requests under 10 seconds) - Ensure you get presented with an error “Too many requests. Please wait xx seconds before trying again.”