Skip to main content

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-api as 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.com in 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.

FieldValueWhy
Count100Requests allowed in one window.
Time window60The window, in seconds.
Keyleave remote_addrThe default, already filled in: one count for each client address.
Rejected code, under Behavior429The 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 BehaviorToo many requests, try again in a minuteThe sentence a refused client reads, sent as JSON.

Select Add plugin at the bottom of the panel. The plugin is saved like this:

The Add limit-count panel with the count per minute and the client address as the key.
limit-count · as the console saves it
{
"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
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-Remaining with the requests left in this window, and X-RateLimit-Reset with 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 429 with {"error_msg":"Too many requests, try again in a minute"}; its headers show X-RateLimit-Remaining: 0 and X-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.

limit-count · as the console saves it
{
"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 429 with {"error_msg":"Too many requests, try again in a minute"}, while another consumer calling from the same address still gets 200 with X-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.

limit-count · as the console saves it
{
"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 answered 503 with 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 503 as 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-Remaining and X-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-For is still counted under the address it comes from, until real-ip is on the route.
G8 dev