UNPKG

15 kBMarkdownView Raw
1# <div align="center"> Express Rate Limit </div>
2
3<div align="center">
4
5[![Tests](https://github.com/nfriedly/express-rate-limit/workflows/Test/badge.svg)](https://github.com/nfriedly/express-rate-limit/actions)
6[![npm version](https://img.shields.io/npm/v/express-rate-limit.svg)](https://npmjs.org/package/express-rate-limit 'View this project on NPM')
7[![npm downloads](https://img.shields.io/npm/dm/express-rate-limit)](https://www.npmjs.com/package/express-rate-limit)
8
9Basic rate-limiting middleware for Express. Use to limit repeated requests to
10public APIs and/or endpoints such as password reset. Plays nice with
11[express-slow-down](https://www.npmjs.com/package/express-slow-down).
12
13</div>
14
15### Alternate Rate Limiters
16
17> This module does not share state with other processes/servers by default. If
18> you need a more robust solution, I recommend using an external store. See the
19> [`stores` section](#store) below for a list of external stores.
20
21This module was designed to only handle the basics and didn't even support
22external stores initially. These other options all are excellent pieces of
23software and may be more appropriate for some situations:
24
25- [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
26- [express-brute](https://www.npmjs.com/package/express-brute)
27- [rate-limiter](https://www.npmjs.com/package/express-limiter)
28
29## Install
30
31From the npm registry:
32
33```sh
34# Using npm
35> npm install express-rate-limit
36# Using yarn or pnpm
37> yarn/pnpm add express-rate-limit
38```
39
40From Github Releases:
41
42```sh
43# Using npm
44> npm install https://github.com/nfriedly/express-rate-limit/releases/download/v{version}/express-rate-limit.tgz
45# Using yarn or pnpm
46> yarn/pnpm add https://github.com/nfriedly/express-rate-limit/releases/download/v{version}/express-rate-limit.tgz
47```
48
49Replace `{version}` with the version of the package that you want to your, e.g.:
50`6.0.0`.
51
52## Usage
53
54This library is provided in ESM as well as CJS forms. To import it in a CJS
55project:
56
57```ts
58const rateLimit = require('express-rate-limit')
59```
60
61To import it in a Typescript/ESM project:
62
63```ts
64import rateLimit from 'express-rate-limit'
65```
66
67### Examples
68
69To use it in an API-only server where the rate-limiter should be applied to all
70requests:
71
72```ts
73import rateLimit from 'express-rate-limit'
74
75const limiter = rateLimit({
76 windowMs: 15 * 60 * 1000, // 15 minutes
77 max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
78 standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
79 legacyHeaders: false, // Disable the `X-RateLimit-*` headers
80})
81
82// Apply the rate limiting middleware to all requests
83app.use(limiter)
84```
85
86To use it in a 'regular' web server (e.g. anything that uses
87`express.static()`), where the rate-limiter should only apply to certain
88requests:
89
90```ts
91import rateLimit from 'express-rate-limit'
92
93const apiLimiter = rateLimit({
94 windowMs: 15 * 60 * 1000, // 15 minutes
95 max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
96 standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
97 legacyHeaders: false, // Disable the `X-RateLimit-*` headers
98})
99
100// Apply the rate limiting middleware to API calls only
101app.use('/api', apiLimiter)
102```
103
104To create multiple instances to apply different rules to different endpoints:
105
106```ts
107import rateLimit from 'express-rate-limit'
108
109const apiLimiter = rateLimit({
110 windowMs: 15 * 60 * 1000, // 15 minutes
111 max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
112 standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
113 legacyHeaders: false, // Disable the `X-RateLimit-*` headers
114})
115
116app.use('/api/', apiLimiter)
117
118const createAccountLimiter = rateLimit({
119 windowMs: 60 * 60 * 1000, // 1 hour
120 max: 5, // Limit each IP to 5 create account requests per `window` (here, per hour)
121 message:
122 'Too many accounts created from this IP, please try again after an hour',
123 standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
124 legacyHeaders: false, // Disable the `X-RateLimit-*` headers
125})
126
127app.post('/create-account', createAccountLimiter, (request, response) => {
128 //...
129})
130```
131
132To use a custom store:
133
134```ts
135import rateLimit from 'express-rate-limit'
136import MemoryStore from 'express-rate-limit/memory-store.js'
137
138const apiLimiter = rateLimit({
139 windowMs: 15 * 60 * 1000, // 15 minutes
140 max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
141 standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
142 store: new MemoryStore(),
143})
144
145// Apply the rate limiting middleware to API calls only
146app.use('/api', apiLimiter)
147```
148
149> **Note:** most stores will require additional configuration, such as custom
150> prefixes, when using multiple instances. The default built-in memory store is
151> an exception to this rule.
152
153### Troubleshooting Proxy Issues
154
155If you are behind a proxy/load balancer (usually the case with most hosting
156services, e.g. Heroku, Bluemix, AWS ELB, Nginx, Cloudflare, Akamai, Fastly,
157Firebase Hosting, Rackspace LB, Riverbed Stingray, etc.), the IP address of the
158request might be the IP of the load balancer/reverse proxy (making the rate
159limiter effectively a global one and blocking all requests once the limit is
160reached) or `undefined`. To solve this issue, add the following line to your
161code (right after you create the express application):
162
163```ts
164app.set('trust proxy', numberOfProxies)
165```
166
167Where `numberOfProxies` is the number of proxies between the user and the
168server. To find the correct number, create a test endpoint that returns the
169client IP:
170
171```ts
172app.set('trust proxy', 1)
173app.get('/ip', (request, response) => response.send(request.ip))
174```
175
176Go to `/ip` and see the IP address returned in the response. If it matches your
177IP address (which you can get by going to http://ip.nfriedly.com/ or
178https://api.ipify.org/), then the number of proxies is correct and the rate
179limiter should now work correctly. If not, then keep increasing the number until
180it does.
181
182For more information about the `trust proxy` setting, take a look at the
183[official Express documentation](https://expressjs.com/en/guide/behind-proxies.html).
184
185## Request API
186
187A `request.rateLimit` property is added to all requests with the `limit`,
188`current`, and `remaining` number of requests and, if the store provides it, a
189`resetTime` Date object. These may be used in your application code to take
190additional actions or inform the user of their status.
191
192The property name can be configured with the configuration option
193`requestPropertyName`
194
195## Configuration options
196
197### `windowMs`
198
199Time frame for which requests are checked/remembered. Also used in the
200`Retry-After` header when the limit is reached.
201
202Note: with non-default stores, you may need to configure this value twice, once
203here and once on the store. In some cases the units also differ (e.g. seconds vs
204miliseconds)
205
206Defaults to `60000` ms (= 1 minute).
207
208### `max`
209
210Max number of connections during `windowMs` milliseconds before sending a 429
211response.
212
213May be a number, or a function that returns a number or a promise. If `max` is a
214function, it will be called with `request` and `response` params.
215
216Defaults to `5`. Set to `0` to disable.
217
218Example of using a function:
219
220```ts
221import rateLimit from 'express-rate-limit'
222
223const isPremium = (request) => {
224 // ...
225}
226
227const limiter = rateLimit({
228 // `max` could also be an async function or return a promise
229 max: (request, response) => {
230 if (isPremium(request)) return 10
231 else return 5
232 },
233 // ...
234})
235
236// Apply the rate limiting middleware to all requests
237app.use(limiter)
238```
239
240### `message`
241
242Error message sent to user when `max` is exceeded.
243
244May be a `string`, JSON object, or any other value that Express's
245[response.send](https://expressjs.com/en/4x/api.html#response.send) method
246supports.
247
248Defaults to `'Too many requests, please try again later.'`
249
250### `statusCode`
251
252HTTP status code returned when `max` is exceeded.
253
254Defaults to `429`.
255
256### `legacyHeaders`
257
258Enable headers for request limit (`X-RateLimit-Limit`) and current usage
259(`X-RateLimit-Remaining`) on all responses and time to wait before retrying
260(`Retry-After`) when `max` is exceeded.
261
262Defaults to `true`.
263
264> Renamed in `6.x` from `headers` to `legacyHeaders`.
265
266### `standardHeaders`
267
268Enable headers conforming to the
269[ratelimit standardization draft](https://github.com/ietf-wg-httpapi/ratelimit-headers/blob/main/draft-ietf-httpapi-ratelimit-headers.md)
270adopted by the IETF: `RateLimit-Limit`, `RateLimit-Remaining`, and, if the store
271supports it, `RateLimit-Reset`. May be used in conjunction with, or instead of
272the `legacyHeaders` option.
273
274This setting also enables the `Retry-After` header when `max` is exceeded.
275
276Defaults to `false` (for backward compatibility), but recommended to use.
277
278> Renamed in `6.x` from `draft_polli_ratelimit_headers` to `standardHeaders`.
279
280### `keyGenerator`
281
282Function used to generate keys.
283
284Defaults to `request.ip`, similar to this:
285
286```ts
287const keyGenerator = (request /*, response*/) => request.ip
288```
289
290### `handler`
291
292The function to handle requests once the max limit is exceeded. It receives the
293`request` and the `response` objects. The `next` param is available if you need
294to pass to the next middleware/route. Finally, the `options` param has all of
295the options that originally passed in when creating the current limiter and the
296default values for other options.
297
298The `request.rateLimit` object has `limit`, `current`, and `remaining` number of
299requests and, if the store provides it, a `resetTime` Date object.
300
301Defaults to:
302
303```ts
304const handler = (request, response, next, options) => {
305 response.status(options.statusCode).send(options.message)
306}
307```
308
309### `requestWasSuccessful`
310
311Function that is called when `skipFailedRequests` and/or
312`skipSuccessfulRequests` are set to `true`. May be overridden if, for example, a
313service sends out a 200 status code on errors.
314
315Defaults to
316
317```ts
318const requestWasSuccessful = (request, response) => response.statusCode < 400
319```
320
321### `skipFailedRequests`
322
323When set to `true`, failed requests won't be counted. Request considered failed
324when:
325
326- response status >= 400
327- requests that were cancelled before last chunk of data was sent (response
328 `close` event triggered)
329- response `error` event was triggered by response
330
331(Technically they are counted and then un-counted, so a large number of slow
332requests all at once could still trigger a rate-limit. This may be fixed in a
333future release.)
334
335Defaults to `false`.
336
337### `skipSuccessfulRequests`
338
339When set to `true` successful requests (response status < 400) won't be counted.
340(Technically they are counted and then un-counted, so a large number of slow
341requests all at once could still trigger a rate-limit. This may be fixed in a
342future release.)
343
344Defaults to `false`.
345
346### `skip`
347
348Function used to skip (whitelist) requests. Returning `true`, or a promise that
349resolves with `true`, from the function will skip limiting for that request.
350
351Defaults to always `false` (count all requests):
352
353```ts
354const skip = (/*request, response*/) => false
355```
356
357### `requestPropertyName`
358
359The name of the property that contains the rate limit information to add to the
360`request` object.
361
362Defaults to `rateLimit`.
363
364### `store`
365
366The storage to use when persisting rate limit attempts.
367
368By default, the [memory store](source/memory-store.ts) is used.
369
370Available data stores are:
371
372- [memory-store](source/memory-store.ts): _(default)_ Simple in-memory option.
373 Does not share state when app has multiple processes or servers.
374- [rate-limit-redis](https://npmjs.com/package/rate-limit-redis): A
375 [Redis](http://redis.io/)-backed store, more suitable for large or demanding
376 deployments.
377- [rate-limit-memcached](https://npmjs.org/package/rate-limit-memcached): A
378 [Memcached](https://memcached.org/)-backed store.
379- [rate-limit-mongo](https://www.npmjs.com/package/rate-limit-mongo): A
380 [MongoDB](https://www.mongodb.com/)-backed store.
381- [precise-memory-rate-limit](https://www.npmjs.com/package/precise-memory-rate-limit) -
382 A memory store similar to the built-in one, except that it stores a distinct
383 timestamp for each IP rather than bucketing them together.
384
385You may also create your own store. It must implement the `Store` interface as
386follows:
387
388```ts
389import rateLimit, {
390 Store,
391 Options,
392 IncrementResponse,
393} from 'express-rate-limit'
394
395/**
396 * A {@link Store} that stores the hit count for each client.
397 *
398 * @public
399 */
400class SomeStore implements Store {
401 /**
402 * Some store-specific parameter.
403 */
404 customParam!: string
405 /**
406 * The duration of time before which all hit counts are reset (in milliseconds).
407 */
408 windowMs!: number
409
410 /**
411 * @constructor for {@link SomeStore}. Only required if the user needs to pass
412 * some store specific parameters. For example, in a Mongo Store, the user will
413 * need to pass the URI, username and password for the Mongo database.
414 *
415 * @param customParam {string} - Some store-specific parameter.
416 */
417 constructor(customParam: string) {
418 this.customParam = customParam
419 }
420
421 /**
422 * Method that actually initializes the store. Must be synchronous.
423 *
424 * @param options {Options} - The options used to setup the middleware.
425 *
426 * @public
427 */
428 init(options: Options): void {
429 this.windowMs = options.windowMs
430
431 // ...
432 }
433
434 /**
435 * Method to increment a client's hit counter.
436 *
437 * @param key {string} - The identifier for a client
438 *
439 * @returns {IncrementResponse} - The number of hits and reset time for that client
440 *
441 * @public
442 */
443 async increment(key: string): Promise<IncrementResponse> {
444 // ...
445
446 return {
447 totalHits,
448 resetTime,
449 }
450 }
451
452 /**
453 * Method to decrement a client's hit counter.
454 *
455 * @param key {string} - The identifier for a client
456 *
457 * @public
458 */
459 async decrement(key: string): Promise<void> {
460 // ...
461 }
462
463 /**
464 * Method to reset a client's hit counter.
465 *
466 * @param key {string} - The identifier for a client
467 *
468 * @public
469 */
470 async resetKey(key: string): Promise<void> {
471 // ...
472 }
473
474 /**
475 * Method to reset everyone's hit counter.
476 *
477 * @public
478 */
479 async resetAll(): Promise<void> {
480 // ...
481 }
482}
483
484export default SomeStore
485```
486
487## Instance API
488
489### `resetKey(key)`
490
491Resets the rate limiting for a given key. An example use case is to allow users
492to complete a captcha or whatever to reset their rate limit, then call this
493method.
494
495## Issues and Contributing
496
497If you encounter a bug or want to see something added/changed, please go ahead
498and [open an issue](https://github.com/nfriedly/express-rate-limit/issues/new)!
499If you need help with something, feel free to
500[start a discussion](https://github.com/nfriedly/express-rate-limit/discussions/new)!
501
502If you wish to contribute to the library, thanks! First, please read
503[the contributing guide](contributing.md). Then you can pick up any issue and
504fix/implement it!
505
506## License
507
508MIT © [Nathan Friedly](http://nfriedly.com/)