2014-08-21 23:51:33 -04:00
|
|
|
# redis-rate-limiter
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-10-03 01:51:12 -04:00
|
|
|
[![NPM](http://img.shields.io/npm/v/redis-rate-limiter.svg?style=flat)](https://npmjs.org/package/redis-rate-limiter)
|
|
|
|
[![License](http://img.shields.io/npm/l/redis-rate-limiter.svg?style=flat)](https://github.com/TabDigital/redis-rate-limiter)
|
|
|
|
|
|
|
|
[![Build Status](http://img.shields.io/travis/TabDigital/redis-rate-limiter.svg?style=flat)](http://travis-ci.org/TabDigital/redis-rate-limiter)
|
|
|
|
[![Dependencies](http://img.shields.io/david/TabDigital/redis-rate-limiter.svg?style=flat)](https://david-dm.org/TabDigital/redis-rate-limiter)
|
|
|
|
[![Dev dependencies](http://img.shields.io/david/dev/TabDigital/redis-rate-limiter.svg?style=flat)](https://david-dm.org/TabDigital/redis-rate-limiter)
|
2016-03-29 00:43:37 -04:00
|
|
|
[![Known Vulnerabilities](https://snyk.io/package/npm/redis-rate-limiter/badge.svg)](https://snyk.io/package/npm/redis-rate-limiter)
|
2014-10-03 01:51:12 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
Rate-limit any operation, backed by Redis.
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
- Inspired by [ratelimiter](https://www.npmjs.org/package/ratelimiter)
|
|
|
|
- But uses a fixed-window algorithm
|
|
|
|
- Great performance (>10000 checks/sec on local redis)
|
|
|
|
- No race conditions
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
Very easy to plug into `Express` or `Restify` to rate limit your `Node.js` API.
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
## Usage
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
Step 1: create a Redis connection
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
```js
|
|
|
|
var redis = require('redis');
|
|
|
|
var client = redis.createClient(6379, 'localhost', {enable_offline_queue: false});
|
2014-08-21 02:20:59 -04:00
|
|
|
```
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
Step 2: create your rate limiter
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
```js
|
|
|
|
var rateLimiter = require('redis-rate-limiter');
|
|
|
|
var limit = rateLimiter.create({
|
|
|
|
redis: client,
|
|
|
|
key: function(x) { return x.id },
|
|
|
|
rate: '100/minute'
|
|
|
|
});
|
|
|
|
```
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
And go
|
|
|
|
|
|
|
|
```js
|
|
|
|
limit(request, function(err, rate) {
|
|
|
|
if (err) {
|
|
|
|
console.warn('Rate limiting not available');
|
|
|
|
} else {
|
|
|
|
console.log('Rate window: ' + rate.window); // 60
|
|
|
|
console.log('Rate limit: ' + rate.limit); // 100
|
|
|
|
console.log('Rate current: ' + rate.current); // 74
|
|
|
|
if (rate.over) {
|
|
|
|
console.error('Over the limit!');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2014-08-21 02:20:59 -04:00
|
|
|
```
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
## Options
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
### `redis`
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
A pre-created Redis client.
|
|
|
|
Make sure offline queueing is disabled.
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
```js
|
|
|
|
var client = redis.createClient(6379, 'localhost', {
|
|
|
|
enable_offline_queue: false
|
|
|
|
});
|
2014-08-21 02:20:59 -04:00
|
|
|
```
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
### `key`
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
The key is how requests are grouped for rate-limiting.
|
|
|
|
Typically, this would be a user ID, a type of operation...
|
2014-08-21 02:20:59 -04:00
|
|
|
There are several helpers built-in:
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
```js
|
|
|
|
// identify users by IP
|
2014-08-21 02:20:59 -04:00
|
|
|
key: 'ip'
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
// identify users by their IP network (255.255.255.0 mask)
|
2014-08-21 02:20:59 -04:00
|
|
|
key: 'ip/32'
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
// identify users by the X-Forwarded-For header
|
|
|
|
// careful: this is just an HTTP header and can easily be spoofed
|
2014-08-21 02:20:59 -04:00
|
|
|
key: 'x-forwarded-for'
|
|
|
|
```
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
You can also specify any custom function:
|
|
|
|
|
|
|
|
```js
|
|
|
|
// rate-limit each user separately
|
|
|
|
key: function(x) { return x.user.id; }
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
// rate limit per user and operation type
|
|
|
|
key: function(x) { return x.user.id + ':' + x.operation; }
|
|
|
|
|
|
|
|
// rate limit everyone in the same bucket
|
|
|
|
key: function(x) { return 'single-bucket'; }
|
2014-08-21 02:20:59 -04:00
|
|
|
```
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
### `window`
|
|
|
|
|
|
|
|
This is the duration over which rate-limiting is applied, in seconds.
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
```js
|
|
|
|
// rate limit per minute
|
|
|
|
window: 60
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
// rate limit per hour
|
|
|
|
window: 3600
|
2014-08-21 02:20:59 -04:00
|
|
|
```
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
Note that this is **not a rolling window**.
|
|
|
|
If you specify `10 requests / minute`, a user would be able
|
|
|
|
to execute 10 requests at `00:59` and another 10 at `01:01`.
|
|
|
|
Then they won't be able to make another request until `02:00`.
|
|
|
|
|
|
|
|
|
|
|
|
### `limit`
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
This is the total number of requests a unique `key` can make during the `window`.
|
|
|
|
|
|
|
|
```js
|
|
|
|
limit: 100
|
2014-08-21 02:20:59 -04:00
|
|
|
```
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
### `rate`
|
|
|
|
|
|
|
|
Rate is a shorthand notation to combine `limit` and `window`.
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
```js
|
|
|
|
rate: '10/second'
|
|
|
|
rate: '100/minute'
|
|
|
|
rate: '1000/hour'
|
|
|
|
```
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
Or the even shorter
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
```js
|
|
|
|
rate: '10/s'
|
|
|
|
rate: '100/m'
|
|
|
|
rate: '100/h'
|
2014-08-21 02:20:59 -04:00
|
|
|
```
|
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
*Note:* the rate is parsed ahead of time, so this notation doesn't affect performance.
|
|
|
|
|
|
|
|
## HTTP middleware
|
|
|
|
|
|
|
|
This package contains a pre-built middleware,
|
|
|
|
which takes the same options
|
2014-08-21 02:20:59 -04:00
|
|
|
|
2014-08-21 23:51:33 -04:00
|
|
|
|
|
|
|
```js
|
|
|
|
var rateLimiter = require('redis-rate-limiter');
|
|
|
|
|
|
|
|
var middleware = rateLimiter.middleware({
|
|
|
|
redis: client,
|
|
|
|
key: 'ip',
|
|
|
|
rate: '100/minute'
|
|
|
|
});
|
|
|
|
|
|
|
|
server.use(middleware);
|
2014-08-21 02:20:59 -04:00
|
|
|
```
|
2014-08-21 23:51:33 -04:00
|
|
|
|
|
|
|
It rejects any rate-limited requests with a status code of `HTTP 429`,
|
|
|
|
and an empty body.
|
2014-08-25 21:00:59 -04:00
|
|
|
|
|
|
|
*Note:* if you want to rate limit several routes individually, don't forget to use the route name as part of the `key`, for example using Restify:
|
|
|
|
|
|
|
|
```js
|
|
|
|
function ipAndRoute(req) {
|
|
|
|
return req.connection.remoteAddress + ':' + req.route.name;
|
|
|
|
}
|
|
|
|
|
|
|
|
server.get(
|
|
|
|
{name: 'routeA', path: '/a'},
|
|
|
|
rateLimiter.middleware({redis: client, key: ipAndRoute, rate: '10/minute'}),
|
|
|
|
controllerA
|
|
|
|
);
|
|
|
|
|
|
|
|
server.get(
|
|
|
|
{name: 'routeB', path: '/b'},
|
|
|
|
rateLimiter.middleware({redis: client, key: ipAndRoute, rate: '20/minute'}),
|
|
|
|
controllerB
|
|
|
|
);
|
|
|
|
|
|
|
|
```
|