Rate limiting
The RateLimit plugin allows you to limit the number of requests a client can make within a certain time period. Ktor provides different means for configuring rate limiting, for example:
You can enable rate limiting globally for a whole application or configure different rate limits for different resources.
You can configure rate limiting based on specific request parameters: an IP address, an API key or access token, and so on.
Add dependencies
To use RateLimit
, you need to include the ktor-server-rate-limit
artifact in the build script:
Install RateLimit
To install the RateLimit
plugin to the application, pass it to the install
function in the specified module. The code snippets below show how to install RateLimit
...
... inside the
embeddedServer
function call.... inside the explicitly defined
module
, which is an extension function of theApplication
class.
Configure RateLimit
Overview
Ktor uses the token bucket algorithm for rate limiting, which works as follows:
In the beginning, we have a bucket defined by its capacity - the number of tokens.
Each incoming request tries to consume one token from the bucket:
If there is enough capacity, the server handles a request and sends a response with the following headers:
X-RateLimit-Limit
: a specified bucket capacity.X-RateLimit-Remaining
: the number of tokens remaining in a bucket.X-RateLimit-Reset
: a UTC timestamp (in seconds) that specifies the time of refilling a bucket.
If there is insufficient capacity, the server rejects a request using a
429 Too Many Requests
response and adds theRetry-After
header, indicating how long the client should wait (in seconds) before making a follow-up request.
After a specified period of time, a bucket capacity is refilled.
Register a rate limiter
Ktor allows you to apply rate limiting globally to a whole application or to specific routes:
To apply rate limiting to a whole application, call the
global
method and pass a configured rate limiter.install(RateLimit) { global { rateLimiter(limit = 5, refillPeriod = 60.seconds) } }The
register
method registers a rate limiter that can be applied to specific routes.install(RateLimit) { register { rateLimiter(limit = 5, refillPeriod = 60.seconds) } }
Code samples above demonstrate minimal configurations for the RateLimit
plugin, but for a rate limiter registered using the register
method you also need to apply it to a specific route.
Configure rate limiting
In this section, we'll see how to configure rate limiting:
(Optional) The
register
method allows you to specify a rate limiter name that can be used to apply rate limiting rules to specific routes:install(RateLimit) { register(RateLimitName("protected")) { // ... } }The
rateLimiter
method creates a rate limiter with two parameters:limit
defines the bucket capacity, whilerefillPeriod
specifies a refill period for this bucket. A rate limiter in the example below allows handling 30 requests per minute:register(RateLimitName("protected")) { rateLimiter(limit = 30, refillPeriod = 60.seconds) }(Optional)
requestKey
allows you to specify a function that returns a key for a request. Requests with different keys have independent rate limits. In the example below, thelogin
query parameter is a key used to distinguish different users:register(RateLimitName("protected")) { requestKey { applicationCall -> applicationCall.request.queryParameters["login"]!! } }(Optional)
requestWeight
sets a function that returns how many tokens are consumed by a request. In the example below, a request key is used to configure a request weight:register(RateLimitName("protected")) { requestKey { applicationCall -> applicationCall.request.queryParameters["login"]!! } requestWeight { applicationCall, key -> when(key) { "jetbrains" -> 1 else -> 2 } } }(Optional)
modifyResponse
allows you to override defaultX-RateLimit-*
headers sent with each request:register(RateLimitName("protected")) { modifyResponse { applicationCall, state -> applicationCall.response.header("X-RateLimit-Custom-Header", "Some value") } }
Define rate limiting scope
After configuring a rate limiter, you can apply its rules to specific routes using the rateLimit
method:
This method can also accept a rate limiter name:
Example
The code sample below demonstrates how to use the RateLimit
plugin to apply different rate limiters to different resources. The StatusPages plugin is used to handle rejected requests, for which the 429 Too Many Requests
response was sent.
You can find the full example here: rate-limit.