title | keywords | description | |||
---|---|---|---|---|---|
limit-count |
|
The limit-count plugin uses a fixed window algorithm to limit the rate of requests by the number of requests within a given time interval. Requests exceeding the configured quota will be rejected. |
The limit-count
plugin uses a fixed window algorithm to limit the rate of requests by the number of requests within a given time interval. Requests exceeding the configured quota will be rejected.
You may see the following rate limiting headers in the response:
X-RateLimit-Limit
: the total quotaX-RateLimit-Remaining
: the remaining quotaX-RateLimit-Reset
: number of seconds left for the counter to reset
Name | Type | Required | Default | Valid values | Description |
---|---|---|---|---|---|
count | integer | True | > 0 | The maximum number of requests allowed within a given time interval. | |
time_window | integer | True | > 0 | The time interval corresponding to the rate limiting count in seconds. |
|
key_type | string | False | var | ["var","var_combination","constant"] | The type of key. If the key_type is var , the key is interpreted a variable. If the key_type is var_combination , the key is interpreted as a combination of variables. If the key_type is constant , the key is interpreted as a constant. |
key | string | False | remote_addr | The key to count requests by. If the key_type is var , the key is interpreted a variable. The variable does not need to be prefixed by a dollar sign ($ ). If the key_type is var_combination , the key is interpreted as a combination of variables. All variables should be prefixed by dollar signs ($ ). For example, to configure the key to use a combination of two request headers custom-a and custom-b , the key should be configured as $http_custom_a $http_custom_b . If the key_type is constant , the key is interpreted as a constant value. |
|
rejected_code | integer | False | 503 | [200,...,599] | The HTTP status code returned when a request is rejected for exceeding the threshold. |
rejected_msg | string | False | non-empty | The response body returned when a request is rejected for exceeding the threshold. | |
policy | string | False | local | ["local","redis","redis-cluster"] | The policy for rate limiting counter. If it is local , the counter is stored in memory locally. If it is redis , the counter is stored on a Redis instance. If it is redis-cluster , the counter is stored in a Redis cluster. |
allow_degradation | boolean | False | false | If true, allow APISIX to continue handling requests without the plugin when the plugin or its dependencies become unavailable. | |
show_limit_quota_header | boolean | False | true | If true, include X-RateLimit-Limit to show the total quota and X-RateLimit-Remaining to show the remaining quota in the response header. |
|
group | string | False | non-empty | The group ID for the plugin, such that routes of the same group can share the same rate limiting counter. |
|
redis_host | string | False | The address of the Redis node. Required when policy is redis . |
||
redis_port | integer | False | 6379 | [1,...] | The port of the Redis node when policy is redis . |
redis_username | string | False | The username for Redis if Redis ACL is used. If you use the legacy authentication method requirepass , configure only the redis_password . Used when policy is redis . |
||
redis_password | string | False | The password of the Redis node when policy is redis or redis-cluster . |
||
redis_ssl | boolean | False | false | If true, use SSL to connect to Redis cluster when policy is redis . |
|
redis_ssl_verify | boolean | False | false | If true, verify the server SSL certificate when policy is redis . |
|
redis_database | integer | False | 0 | >= 0 | The database number in Redis when policy is redis . |
redis_timeout | integer | False | 1000 | [1,...] | The Redis timeout value in milliseconds when policy is redis or redis-cluster . |
redis_cluster_nodes | array[string] | False | The list of the Redis cluster nodes with at least two addresses. Required when policy is redis-cluster. | ||
redis_cluster_name | string | False | The name of the Redis cluster. Required when policy is redis-cluster . |
||
redis_cluster_ssl | boolean | False | false | If true, use SSL to connect to Redis cluster when policy is redis-cluster . |
|
redis_cluster_ssl_verify | boolean | False | false | If true, verify the server SSL certificate when policy is redis-cluster . |
The examples below demonstrate how you can configure limit-count
in different scenarios.
:::note
You can fetch the admin_key
from config.yaml
and save to an environment variable with the following command:
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')
:::
The following example demonstrates the rate limiting of requests by a single variable, remote_addr
.
Create a Route with limit-count
plugin that allows for a quota of 1 within a 30-second window per remote address:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "limit-count-route",
"uri": "/get",
"plugins": {
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key_type": "var",
"key": "remote_addr"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Send a request to verify:
curl -i "http://127.0.0.1:9080/get"
You should see an HTTP/1.1 200 OK
response.
The request has consumed all the quota allowed for the time window. If you send the request again within the same 30-second time interval, you should receive an HTTP/1.1 429 Too Many Requests
response, indicating the request surpasses the quota threshold.
The following example demonstrates the rate limiting of requests by a combination of variables, remote_addr
and consumer_name
. It allows for a quota of 1 within a 30-second window per remote address and for each consumer.
Create a Consumer john
:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"username": "john"
}'
Create key-auth
Credential for the consumer:
curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "cred-john-key-auth",
"plugins": {
"key-auth": {
"key": "john-key"
}
}
}'
Create a second Consumer jane
:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"username": "jane"
}'
Create key-auth
Credential for the Consumer:
curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "cred-jane-key-auth",
"plugins": {
"key-auth": {
"key": "jane-key"
}
}
}'
Create a Route with key-auth
and limit-count
plugins, and specify in the limit-count
plugin to use a combination of variables as the rate limiting key:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "limit-count-route",
"uri": "/get",
"plugins": {
"key-auth": {},
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key_type": "var_combination",
"key": "$remote_addr $consumer_name"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Send a request as the Consumer jane
:
curl -i "http://127.0.0.1:9080/get" -H 'apikey: jane-key'
You should see an HTTP/1.1 200 OK
response with the corresponding response body.
This request has consumed all the quota set for the time window. If you send the same request as the Consumer jane
within the same 30-second time interval, you should receive an HTTP/1.1 429 Too Many Requests
response, indicating the request surpasses the quota threshold.
Send the same request as the Consumer john
within the same 30-second time interval:
curl -i "http://127.0.0.1:9080/get" -H 'apikey: john-key'
You should see an HTTP/1.1 200 OK
response with the corresponding response body, indicating the request is not rate limited.
Send the same request as the Consumer john
again within the same 30-second time interval, you should receive an HTTP/1.1 429 Too Many Requests
response.
This verifies the plugin rate limits by the combination of variables, remote_addr
and consumer_name
.
The following example demonstrates the sharing of rate limiting quota among multiple routes by configuring the group
of the limit-count
plugin.
Note that the configurations of the limit-count
plugin of the same group
should be identical. To avoid update anomalies and repetitive configurations, you can create a Service with limit-count
plugin and Upstream for routes to connect to.
Create a service:
curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "limit-count-service",
"plugins": {
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"group": "srv1"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Create two Routes and configure their service_id
to be limit-count-service
, so that they share the same configurations for the Plugin and Upstream:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "limit-count-route-1",
"service_id": "limit-count-service",
"uri": "/get1",
"plugins": {
"proxy-rewrite": {
"uri": "/get"
}
}
}'
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "limit-count-route-2",
"service_id": "limit-count-service",
"uri": "/get2",
"plugins": {
"proxy-rewrite": {
"uri": "/get"
}
}
}'
:::note
The proxy-rewrite
plugin is used to rewrite the URI to /get
so that requests are forwarded to the correct endpoint.
:::
Send a request to Route /get1
:
curl -i "http://127.0.0.1:9080/get1"
You should see an HTTP/1.1 200 OK
response with the corresponding response body.
Send the same request to Route /get2
within the same 30-second time interval:
curl -i "http://127.0.0.1:9080/get2"
You should receive an HTTP/1.1 429 Too Many Requests
response, which verifies the two routes share the same rate limiting quota.
The following example demonstrates the rate limiting of requests across multiple APISIX nodes with a Redis server, such that different APISIX nodes share the same rate limiting quota.
On each APISIX instance, create a Route with the following configurations. Adjust the address of the Admin API, Redis host, port, password, and database accordingly.
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "limit-count-route",
"uri": "/get",
"plugins": {
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key": "remote_addr",
"policy": "redis",
"redis_host": "192.168.xxx.xxx",
"redis_port": 6379,
"redis_password": "p@ssw0rd",
"redis_database": 1
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Send a request to an APISIX instance:
curl -i "http://127.0.0.1:9080/get"
You should see an HTTP/1.1 200 OK
response with the corresponding response body.
Send the same request to a different APISIX instance within the same 30-second time interval, you should receive an HTTP/1.1 429 Too Many Requests
response, verifying routes configured in different APISIX nodes share the same quota.
You can also use a Redis cluster to apply the same quota across multiple APISIX nodes, such that different APISIX nodes share the same rate limiting quota.
Ensure that your Redis instances are running in cluster mode. A minimum of two nodes are required for the limit-count
plugin configurations.
On each APISIX instance, create a Route with the following configurations. Adjust the address of the Admin API, Redis cluster nodes, password, cluster name, and SSL varification accordingly.
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "limit-count-route",
"uri": "/get",
"plugins": {
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key": "remote_addr",
"policy": "redis-cluster",
"redis_cluster_nodes": [
"192.168.xxx.xxx:6379",
"192.168.xxx.xxx:16379"
],
"redis_password": "p@ssw0rd",
"redis_cluster_name": "redis-cluster-1",
"redis_cluster_ssl": true
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Send a request to an APISIX instance:
curl -i "http://127.0.0.1:9080/get"
You should see an HTTP/1.1 200 OK
response with the corresponding response body.
Send the same request to a different APISIX instance within the same 30-second time interval, you should receive an HTTP/1.1 429 Too Many Requests
response, verifying routes configured in different APISIX nodes share the same quota.
does not need to authenticate and has less quotas. While this example uses key-auth
for authentication, the anonymous Consumer can also be configured with basic-auth
, jwt-auth
, and hmac-auth
.
Create a regular Consumer john
and configure the limit-count
plugin to allow for a quota of 3 within a 30-second window:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"username": "john",
"plugins": {
"limit-count": {
"count": 3,
"time_window": 30,
"rejected_code": 429
}
}
}'
Create the key-auth
Credential for the Consumer john
:
curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "cred-john-key-auth",
"plugins": {
"key-auth": {
"key": "john-key"
}
}
}'
Create an anonymous user anonymous
and configure the limit-count
Plugin to allow for a quota of 1 within a 30-second window:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"username": "anonymous",
"plugins": {
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429
}
}
}'
Create a Route and configure the key-auth
Plugin to accept anonymous Consumer anonymous
from bypassing the authentication:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "key-auth-route",
"uri": "/anything",
"plugins": {
"key-auth": {
"anonymous_consumer": "anonymous"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
To verify, send five consecutive requests with john
's key:
resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'apikey: john-key' -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429
You should see the following response, showing that out of the 5 requests, 3 requests were successful (status code 200) while the others were rejected (status code 429).
200: 3, 429: 2
Send five anonymous requests:
resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429
You should see the following response, showing that only one request was successful:
200: 1, 429: 4