Limit requests on a route
limit-count counts the requests that reach a route and refuses the ones over a limit. It counts under a key, the value that tells callers apart. By default that is the client address. This guide allows each client address 100 requests a minute on orders-api, then shows how to count per consumer instead and how to share one count across several gateway nodes. At the end, each client address gets 100 requests a minute on orders-api, and the next request within that minute is answered 429.
Before you start
- This guide starts from
orders-apias Route requests to a backend leaves it, with key-auth from Require an API key. The requests below call/orders/1001. A plugin from another guide that this page does not name can change the answers below; remove it first or work on a copy of the route. - The host name of your gateway. In the console it is the first part of the Address line on a route's Overview tab,
api.example.comin this page's examples; replace it in every request below. - Whether a load balancer sits in front of the gateway. If one does, every client arrives under the load balancer's address, and one count would cover all of them. Count per consumer, as the section after the steps shows, or follow See each client's own address first.
- How many gateway nodes the environment has. With one node, one count. With several, each node counts on its own unless you share the count through Redis, as the last section shows.
- The Editor or Environment administrator role in this environment.
Steps
1. Choose limit-count
Open orders-api under Routes, select its Plugins tab, then Add plugin at the top right of the tab. Open the Traffic category, or type limit-count in the search box. Select limit-count, then Configure.
2. Set the limit
Under Basics, keep One limit for everything here chosen. The other choice, A limit for each key, is for several limits at once and is not needed here. Fill in these fields and leave the rest as they are.
| Field | Value | Why |
|---|---|---|
| Count | 100 | Requests allowed in one window. |
| Time window | 60 | The window, in seconds. |
| Key | leave remote_addr | The default, already filled in: one count for each client address. |
| Rejected code, under Behavior | 429 | The status a refused request gets. Unset, the gateway answers 503 with an HTML error page, the answer of a service that is down. |
| Rejected message, under Behavior | Too many requests, try again in a minute | The sentence a refused client reads, sent as JSON. |
Select Add plugin at the bottom of the panel. The plugin is saved like this:
{
"count": 100,
"time_window": 60,
"key": "remote_addr",
"rejected_code": 429,
"rejected_msg": "Too many requests, try again in a minute"
}
Check it
Send the request from one machine, then keep sending it until the limit is reached. key-auth is on the route, so every request carries the key of mobile-app in the x-api-key header. A shell loop does that:
for i in $(seq 101); do curl -s -o /dev/null -w '%{http_code}\n' -H 'x-api-key: <the key of mobile-app>' 'https://<gateway host>/orders/1001'; done
The single request, with its headers, is below. Add the same x-api-key header to it.
curl -i -L 'https://<gateway host>/orders/1001'
- Within the limit
- A request within the count reaches your backend, and its answer carries
X-RateLimit-Limit: 100,X-RateLimit-Remainingwith the requests left in this window, andX-RateLimit-Resetwith the seconds left in it. - The first request of a window
- The window opens with an address's first request, not at a round minute on the clock: that first answer carries
X-RateLimit-Reset: 60, the seconds until the window ends. - Over the limit
- The 101st request in a window from one address is answered
429with{"error_msg":"Too many requests, try again in a minute"}; its headers showX-RateLimit-Remaining: 0andX-RateLimit-Reset, the seconds until the window ends. - The same address, on another gateway node
- With the default Policy each gateway node counts on its own: once one node has refused an address, the same request to another node is answered with
X-RateLimit-Remaining: 99.
Count per consumer instead
When each client has an API key, count by the consumer the key names. Two consumers calling from one address then get a count each, and clients behind one load balancer no longer share one. The consumer is named by key-auth, which the route carries from the starting point.
On the Plugins tab of orders-api, select limit-count in the list of plugins, then Edit. Type consumer_name into Key and select Save plugin at the bottom of the panel. Each client's requests then carry its own key in the x-api-key header.
{
"count": 100,
"time_window": 60,
"key": "consumer_name",
"rejected_code": 429,
"rejected_msg": "Too many requests, try again in a minute"
}
- Two consumers from one address
- Each consumer has 100 requests a minute of its own: once one has used them, its next request is answered
429with{"error_msg":"Too many requests, try again in a minute"}, while another consumer calling from the same address still gets200withX-RateLimit-Remaining: 99.
Count once across gateway nodes
Each gateway node keeps its own count. A client whose requests reach two nodes gets 100 on each. To hold the client to one count, select limit-count in the list of plugins, then Edit. In the Behavior section, choose redis under Policy instead of the default local.
The fields for the Redis server's address and password appear when you choose it. Fill them in with the address, port and password of a Redis your team runs. Select Save plugin at the bottom of the panel.
The example below names redis.internal on port 6379, the address of a Redis its team runs. It shows a count of 1000 and no Rejected code, so its refusal is 503. Yours shows your own Redis, the count of 100 and the code 429 from step 2, and refuses with 429.
{
"count": 1000,
"time_window": 60,
"key": "remote_addr",
"policy": "redis",
"redis_host": "redis.internal",
"redis_port": 6379,
"redis_password": "<secret>"
}
- Every answer
- Every answer carries
X-RateLimit-Limit. A request beyond the count is answered503with an HTML error page, because this example sets no Rejected code and no Rejected message. - The same address, on another gateway node
- Every gateway node reads the same count: once one node has refused an address, the next request from it to another node is answered
503as well.
What to know
- The window opens with a client's first request and lasts the Time window from then. It does not follow the clock.
- Every answer within the limit carries
X-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Reset, the seconds until the window ends. - Behind a load balancer every client arrives under the load balancer's address. A request that names another client in
X-Forwarded-Foris still counted under the address it comes from, until real-ip is on the route.
Related
- Limit one client wherever it calls: one limit for one consumer over every route it calls.
- Give a group of clients one limit: the same limit for every consumer in a group.
- Apply a plugin to every request: one limit for every request of the environment, on a global rule.
- Share plugins between routes: one limit that several routes share, through a service.
- limit-count in the reference, with the Redis cluster and sentinel shapes under More scenarios.