# Contributing
Source: https://express-rate-limit.mintlify.app/guides/contributing
Thanks for your interest in contributing to Express Rate Limit! This guide will
show you how to set up your environment and contribute to this library.
First, you need to install and be familiar the following:
1. `git`
[Here](https://github.com/git-guides) is a great guide by GitHub on installing
and getting started with Git.
2. `node` and `pnpm`
[This guide](https://nodejs.org/en/download/package-manager/) will help you
install `node` and `npm`. The recommended method is using the `n` version
manager if you are on MacOS or Linux. Make sure you are using the
[active LTS version](https://github.com/nodejs/Release#release-schedule) of
Node.
Next [install pnpm](https://pnpm.io/installation).
Follow
[these instructions](https://docs.github.com/en/get-started/quickstart/fork-a-repo)
to
[fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks)
and [clone](https://github.com/git-guides/git-clone) the repository
(`express-rate-limit/express-rate-limit`).
Once you have forked and cloned the repository, you can
[pick out an issue](https://github.com/express-rate-limit/express-rate-limit/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)
you want to fix/implement!
Run `pnpm install --frozen-lockfile` to install the JavaScript dependencies.
If there are errors, try deleting the `node_modules` folder first, then re-run
the install command.
Once you have cloned the repository to your computer (say, in
`~/Code/express-rate-limit`) and picked the issue you want to tackle, create a
branch based off the `main` branch:
```sh terminal theme={null}
> git switch main
> git switch --create branch-name
```
While naming your branch, make sure the name is short and self explanatory.
Once you have created a branch, you can start coding!
The library is written in `typescript` and supports `node` versions 16, 18
and 20. The code is arranged as follows:
```sh theme={null}
express-rate-limit/
├── config/
│ └── husky/
│ └── pre-commit # runs the linter on staged files
├── docs/
│ └── * # documentation & changelog
├── source/
│ ├── headers.ts # header parsing functions
│ ├── index.ts # exports the types and the middleware
| ├── ip-key-generator.ts # helper function for IPv6
│ ├── rate-limit.ts # option parser and the rate limiting middleware
│ ├── memory-store.ts # the used-by-default memory store
│ ├── types.ts # typescript type definitions, exported as public api
│ ├── utils.ts # utility functions that don't belong elsewhere
│ └── validations.ts # validation checks built into library
├── test/
│ ├── external/
│ │ ├── imports/ # ensures the function can be imported in diff envs
│ │ ├── stores/ # ensures the library works with the store
│ │ └── run-all-tests # runs all the external and store tests
│ └── library
│ ├── helpers
│ │ └── create-server.ts # creates a test express server
│ └── *-test.ts # tests for each file in the `source/` folder
├── license.md # license info
├── pnpm-lock.yml # pnpm lock file, do not modify manually
├── package.json # node package info
├── readme.md # project info
├── jest.config.json # test runner config
└── tsconfig.json # typescript config
└── ... # misclaneous other configuration files
```
Most files have a little description of what they do at the top.
When adding a new feature or fixing a bug, please update the documentation and
changelog as well as add tests for the same. Also make sure the codebase passes
the linter and library tests by running `npm test`. Note that running
`npm run format` will automatically resolve most style/lint issues.
Note that the external tests require various datastores to be installed locally
and take more time to execute. Typically they are run only on GitHub Actions.
You may run these tests locally by running `npm run test:ext`.
Once you have made changes to the code, you will want to
[commit](https://github.com/git-guides/git-commit) (basically, Git's version of
save) the changes. To commit the changes you have made locally:
```sh terminal theme={null}
> git add this/folder that-file.js
> git commit --message 'commit-message'
```
While writing the `commit-message`, try to follow the below guidelines:
1. Prefix the message with `type:`, where `type` is one of the following
depending on what the commit does:
* `fix`: Introduces a bug fix.
* `feat`: Adds a new feature.
* `test`: Any change related to tests.
* `perf`: Any performance related change.
* `meta`: Any change related to the build process, workflows, issue
templates, etc.
* `refc`: Any refactoring work.
* `docs`: Any documentation related changes.
2. Keep the first line brief, and less than 60 characters.
3. Try describing the change in detail in a new paragraph (double newline after
the first line).
When you commit files, `husky` and `lint-staged` will automatically lint the
code and fix most issues. In case an error is not automatically fixable, they
will cancel the commit. Please fix the errors before committing the changes. If
you still wish to commit the changes, prefix the `git commit` command with
`HUSKY=0`, like so:
```sh terminal theme={null}
> HUSKY=0 git commit --message 'commit-message'
```
Once you have committed your changes, you will want to
[push](https://github.com/git-guides/git-push) your commits (basically, publish
your changes to GitHub). To do so, run:
```sh terminal theme={null}
> git push origin branch-name
```
If there are changes made to the `main` branch of the
`express-rate-limit/express-rate-limit` repository, you may wish to merge those
changes into your branch. To do so, run:
```sh terminal theme={null}
> git fetch upstream main
> git merge upstream/main
```
This will automatically add the changes from `main` branch of the
`express-rate-limit/express-rate-limit` repository to the current branch. If you
encounter any merge conflicts, follow
[this guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line)
to resolve them.
Once you have pushed your changes to your fork, follow
[these instructions](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork)
to open a
[pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests).
Once you have submitted a pull request, the maintainers of the repository will
review your pull requests. Whenever a maintainer reviews a pull request they may
request changes. These may be small, such as fixing a typo, or may involve
substantive changes. Such requests are intended to be helpful, but at times may
come across as abrupt or unhelpful, especially if they do not include concrete
suggestions on how to change them. Try not to be discouraged. If you feel that a
review is unfair, say so or seek the input of another project contributor. Often
such comments are the result of a reviewer having taken insufficient time to
review and are not ill-intended. Such difficulties can often be resolved with a
bit of patience. That said, reviewers should be expected to provide helpful
feedback.
In order to land, a pull request needs to be reviewed and approved by at least
one maintainer and pass CI. After that, if there are no objections from other
contributors, the pull request can be merged.
**Congratulations and thanks for your contribution!**
> This contributing guide was inspired by the Electron project's contributing
> guide.
# Creating a Store
Source: https://express-rate-limit.mintlify.app/guides/creating-a-store
### Overview
A store tracks how many hits each client (identified via their IP address) has
received and automatically reduce that hit count as time elapses.
### The `Store` Interface
A store **must** have the `increment`, `decrement`, and `resetKey` public
methods. It may optionally have the `init`, `get` and `resetAll` public methods
and a `prefix` (string) or `localKeys` (boolean) field. For backwards
compatibility with versions prior to `6.0.0`, it may also have the `incr` and
`decr` public methods. Finally, it may have a `constructor` and any number of
private methods.
The `increment` method is the primary interface between the middleware and the
store. It adds 1 to the internal count for a key and returns an object
consisting of the new internal count (`totalHits`) and the time that the count
will reach 0 (`resetTime`).
The `decrement` method is used only to 'uncount' requests when one or both of
the `skipSuccessfulRequests` or `skipFailedRequests` options are enabled.
The `init` method allows the store to set itself up using the options passed to
the middleware. The store can get the `windowMs` option from this method.
Starting with version 8.5.0, the `init` method may be async, and is a good place
for things like opening a connection to a database or ensuring the schema is
up-to-date. Errors & promise rejections from `init` will be caught and logged by
express-rate-limit, but will not prevent other methods such as `increment()`
from being called. Additionally, other methods such as `increment()` may be
called before `init()` completes.
The `get` method takes a `string` argument (the key that identifies a client)
and returns an object consisting of the internal hit count (`totalHits`) and the
time that the count will reach 0 (`resetTime`) for the given client. It may
return `undefined` if it cannot find the key.
The `resetKey` method takes a `string` argument (the key that identifies a
client) and sets the internal count for that key to zero.
The `resetAll` method takes no arguments and sets the internal count for all
keys to zero.
The `prefix` field is used to avoid conflicts when the user creates multiple
instances of the store for multiple rate limits (e.g. 10 hits per minute and 60
hits per hour). Keys in the database should be prefixed with this value.
`prefix` is generally passed as an option to the constructor. (The `singleCount`
validation check also takes the `prefix` field into account and does not report
that a user is being double-counted if the stores have different prefixes.)
The `localKeys` field is an alternative to `prefix` for stores such as the
MemoryStore where two instances will automatically keep separate counts. Setting
it to `true` will prevent false positives from the `singleCount` validation
check.
The `get` and `resetKey` methods can be called from the middleware, like so:
```ts theme={null}
// Create a rate limiter.
const limiter = rateLimit({/* ... */})
// Fetch or reset the hit count for a key.
limiter.get('1.2.3.4')
limiter.resetKey('1.2.3.4')
```
### Dependency configuration
Add `express-rate-limit` as a peer dependency, and a development dependency to
the package:
```json package.json theme={null}
{
"peerDependencies": {
"express-rate-limit": ">= 6"
},
"devDependencies": {
"express-rate-limit": "*"
}
}
```
If the store supports the `incr` method, replace `>= 6` with `>= 2.3.0`
### Example Typescript and Javascript Stores
```ts typescript-store.ts theme={null}
import type {
Store,
Options,
IncrementResponse,
ClientRateLimitInfo,
} from 'express-rate-limit'
type SomeStoreOptions = {
/**
* Optional field to differentiate hit countswhen multiple rate-limits are in use
*/
prefix?: string
/**
* Some store-specific parameter
*/
customParam: string
}
/**
* A `Store` that stores the hit count for each client.
*
* @public
*/
class SomeStore implements Store {
/**
* Some store-specific parameter.
*/
customParam!: string
/**
* The duration of time before which all hit counts are reset (in milliseconds).
*/
windowMs!: number
prefix!: string
/**
* @constructor for `SomeStore`. Only required if the user needs to pass
* some store specific parameters. For example, in a Mongo Store, the user will
* need to pass the URI, username and password for the Mongo database.
*
* Accepting a custom `prefix` here is also recommended.
*
* @param options {SomeStoreOptions} - Prefix and any store-specific parameters.
*/
constructor(options: SomeStoreOptions) {
this.customParam = options.customParam
this.prefix = options.prefix ?? 'rl_'
}
/**
* Method that actually initializes the store.
*
* This method is optional, it will be called only if it exists.
*
* @param options {Options} - The options used to setup express-rate-limit.
*
* @public
*/
async init(options: Options): Promise {
this.windowMs = options.windowMs
// ...
}
/**
* Method to prefix the keys with the given text.
*
* Call this from get, increment, decrement, resetKey, etc.
*
* @param key {string} - The key.
*
* @returns {string} - The text + the key.
*/
prefixKey(key: string): string {
return `${this.prefix}${key}`
}
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*
* @public
*/
async get(key: string): Promise {
// ...
return {
totalHits,
resetTime,
}
}
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {IncrementResponse} - The number of hits and reset time for that client.
*
* @public
*/
async increment(key: string): Promise {
// ...
return {
totalHits,
resetTime,
}
}
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
async decrement(key: string): Promise {
// ...
}
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
async resetKey(key: string): Promise {
// ...
}
/**
* Method to reset everyone's hit counter.
*
* This method is optional, it is never called by express-rate-limit.
*
* @public
*/
async resetAll(): Promise {
// ...
}
}
// Export the store so others can use it
export default SomeStore
```
```js javascript-store.js theme={null}
/**
* A `Store` that stores the hit count for each client.
*
* @public
*/
class SomeStore {
/**
* @constructor for `SomeStore`. Only required if the user needs to pass
* some store specific parameters. For example, in a Mongo Store, the user will
* need to pass the URI, username and password for the Mongo database.
*
* Accepting a custom `prefix` here is also recommended.
*
* @param options {SomeStoreOptions} - Prefix and any store-specific parameters.
*/
constructor({ customParam, prefix }) {
this.customParam = options.customParam
this.prefix = options.prefix ?? 'rl_'
}
/**
* Method that actually initializes the store.
*
* This method is optional, it will be called only if it exists.
*
* @param options {Options} - The options used to setup express-rate-limit.
*
* @public
*/
async init(options) {
this.windowMs = options.windowMs
}
/**
* Method to prefix the keys with the given text.
*
* Call this from get, increment, decrement, resetKey, etc.
*
* @param key {string} - The key.
*
* @returns {string} - The text + the key.
*/
prefixKey(key) {
return `${this.prefix}${key}`
}
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*
* @public
*/
async get(key) {
// ...
return {
totalHits,
resetTime,
}
}
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {IncrementResponse} - The number of hits and reset time for that client.
*
* @public
*/
async increment(key) {
// ...
return {
totalHits, // A positive integer
resetTime, // A JS `Date` object
}
}
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
async decrement(key) {
// ...
}
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
async resetKey(key) {
// ...
}
/**
* Method to reset everyone's hit counter.
*
* This method is optional, it is never called by express-rate-limit.
*
* @public
*/
async resetAll() {
// ...
}
}
// Export the store so others can use it
// ...via the CommonJS style
module.exports = SomeStore
// ...or the ES Module style
export default SomeStore
```
### Using a Custom Store
```ts theme={null}
// Use `const { rateLimit } = require('...')` instead if you are using CommonJS
import { rateLimit } from 'express-rate-limit'
import SomeStore from './some-store.js'
const limiter = rateLimit({
store: new SomeStore({ customParam: '🎉' }),
})
```
# Debugging
Source: https://express-rate-limit.mintlify.app/guides/debugging
Express Rate Limit supports debug logging similar to
[Express](https://expressjs.com/en/5x/guide/debugging/) via the
[debug](https://www.npmjs.com/package/debug) library.
## Basic debugging
To see all the internal logs from Express Rate Limit, set the `DEBUG`
environment variable to `express-rate-limit` when launching your app:
```bash theme={null}
$ DEBUG=express-rate-limit node index.js
```
On Windows, use the corresponding command:
```bash theme={null}
> $env:DEBUG = "express-rate-limit"; node index.js
```
Running this command on a basic configuration prints output similar to the
following at instance creation:
```bash theme={null}
$ DEBUG=express-rate-limit node server.js
express-rate-limit creating new rate limiter with 'MemoryStore' +0ms
express-rate-limit set windowMs to 60000 +0ms
express-rate-limit set limit to 5 +0ms
express-rate-limit set message to 'Too many requests, please try again later.' +0ms
express-rate-limit set statusCode to 429 +0ms
express-rate-limit set legacyHeaders to true +0ms
express-rate-limit set identifier to [Function: identifier] +0ms
express-rate-limit set requestPropertyName to 'rateLimit' +0ms
express-rate-limit set skipFailedRequests to false +0ms
express-rate-limit set skipSuccessfulRequests to false +0ms
express-rate-limit set requestWasSuccessful to [Function: requestWasSuccessful] +0ms
express-rate-limit set skip to [Function: skip] +0ms
express-rate-limit set keyGenerator to [AsyncFunction: keyGenerator] +0ms
express-rate-limit set ipv6Subnet to 56 +0ms
express-rate-limit set handler to [AsyncFunction: handler] +0ms
express-rate-limit set passOnStoreError to false +0ms
express-rate-limit set standardHeaders to false +1ms
express-rate-limit set store to MemoryStore { validations: { default: true, validationsConfig: false, knownOptions: false, draftPolliHeaders: false, onLimitReached: false, keyGeneratorIpFallback: false, ipv6SubnetOrKeyGenerator: false }, previous: Map(0) {}, current: Map(0) {}, localKeys: true } +0ms
express-rate-limit set validations to { default: true, validationsConfig: false, knownOptions: false, draftPolliHeaders: false, onLimitReached: false, keyGeneratorIpFallback: false, ipv6SubnetOrKeyGenerator: false } +0ms
express-rate-limit set logger to { warn: [Function: warn], error: [Function: error] } +0ms
express-rate-limit executing init for store +1ms
```
When a request is then made to the app, you will see the logs similar to these:
```bash theme={null}
express-rate-limit requested '/' +5s
express-rate-limit request from ip '::ffff:127.0.0.1' +0ms
express-rate-limit computed key '127.0.0.1' +0ms
express-rate-limit incrementing count +0ms
express-rate-limit set request.rateLimit.limit to be 5 +0ms
express-rate-limit set request.rateLimit.used to be 1 +0ms
express-rate-limit set request.rateLimit.remaining to be 4 +0ms
express-rate-limit set request.rateLimit.resetTime to be 2026-07-16T09:46:11.574Z +0ms
express-rate-limit set request.rateLimit.key to be '127.0.0.1' +0ms
express-rate-limit set legacy headers +0ms
```
## Other logs and advanced usage
Set the value to `*` to see all debug logs from Express and anything else that
uses the same debug convention.
See [https://www.npmjs.com/package/debug](https://www.npmjs.com/package/debug) for more advanced usage.
# Troubleshooting Proxy Issues
Source: https://express-rate-limit.mintlify.app/guides/troubleshooting-proxy-issues
### The Global Limiter Problem
If you are behind a proxy/load balancer (usually the case with most hosting
services, e.g. Heroku, Bluemix, AWS ELB, Nginx, Cloudflare, Akamai, Fastly,
Firebase Hosting, Rackspace LB, Riverbed Stingray, etc.), the IP address of the
request might be the IP of the load balancer/reverse proxy (making the rate
limiter effectively a global one and blocking all requests once the limit is
reached) or `undefined`.
To solve this issue, assuming the proxy (or proxies) set the `X-Forwarded-For`
header, please follow the steps given below.
Add the following line to your code, right after you create the express
application:
```ts theme={null}
app.set('trust proxy', 1 /* number of proxies between user and server */)
```
To find the correct number of proxies between the user and the server,
create a test endpoint that returns the client IP:
```ts theme={null}
app.get('/ip', (request, response) => {
response.send(request.ip);
});
```
Make a `get` request to `/ip` and check the IP address returned in the
response. If it matches your IP address (which you can get by visiting
[ip.nfriedly.com](http://ip.nfriedly.com/) or
[api.ipify.org](https://api.ipify.org/), then the number of proxies is
correct and the rate limiter should now work correctly. If not, then keep
increasing the number until it does.
For more information about the `trust proxy` setting, take a look at the
[official Express documentation](https://expressjs.com/en/guide/behind-proxies.html).
#### Forwarded header
If the server instead sets the `Forwarded` header, the steps are similar except
that you'll need a
[custom keyGenerator](/reference/error-codes#err-erl-forwarded-header) because
express does not have built-in support for the `Forwarded` header (as of version
5.1.0),
### Port Numbers in IP Addresses
Sometimes, a problem arises because the format of the `X-Forwarded-For` header
isn't standardized between every reverse proxy out there, and Express takes the
trusted value verbatim and sets it as `request.ip`.
While some reverse proxy pass a comma delimited list of IP address, some proxies
(for example, Azure's Application Gateway) will pass a comma delimited list of
`IP:PORT` instead, where the port is the source port, that can change on every
request. Because of this, a user can simply close and re-open their browser to
bypass the rate limit timer as the source port of their HTTP request will
change, even if their IP is the same. This could also be automated in some kind
of script for API abuse.
As a workaround, you could strip the port number from the IP address by using a
custom `keyGenerator` function:
```ts theme={null}
keyGenerator(request: Request, _response: Response): string {
if (!request.ip) {
console.error('Warning: request.ip is missing!')
return request.socket.remoteAddress
}
return request.ip.replace(/:\d+[^:]*$/, '')
}
```
See issue [#234](https://github.com/nfriedly/express-rate-limit/issues/234) for
more info.
# Overview
Source: https://express-rate-limit.mintlify.app/overview
> Thanks to
> [Mintlify](https://mintlify.com/?utm_campaign=devmark\&utm_medium=docs\&utm_source=express-rate-limit),
> for generously hosting this documentation.
Express Rate Limit is a basic rate-limiting middleware for
[Express](http://expressjs.com/), used to limit repeated requests to public APIs
and/or endpoints such as password reset. Plays nice with
[express-slow-down](https://www.npmjs.com/package/express-slow-down) and
[ratelimit-header-parser](https://www.npmjs.com/package/ratelimit-header-parser).
* GitHub: [https://github.com/express-rate-limit/express-rate-limit](https://github.com/express-rate-limit/express-rate-limit)
* npm: [https://www.npmjs.com/package/express-rate-limit](https://www.npmjs.com/package/express-rate-limit)
## Use Cases
This library uses 'stores', which allows for the hit count and reset time of all
clients to be stored in-memory or in an external database. Depending on the use
case, an external store may be needed.
### Abuse Prevention
The default
[memory-store](https://github.com/express-rate-limit/express-rate-limit/blob/main/source/memory-store.ts)
is probably fine.
The default `MemoryStore` keeps the hit counts for clients in memory, and thus
produces inconsistent results when running multiple servers or processes.
When running multiple instances of the memory store, the ratelimiter will always
allow *at least* the configured `max` number of hits through, and *at most* the
configured `max` multiplied by the number of instances. Depending on how
requests are routed in your stack, requests may be intermittently allowed or
blocked after reaching `max`.
### API Rate Limit Enforcement
If you have multiple servers, or want to maintain state across app restarts, use
an [external data store](/reference/stores).
If you have multiple processes on a single server (via the
[node:cluster](https://nodejs.org/api/cluster.html) module), you could use the
[cluster-memory-store](https://npmjs.com/package/@express-rate-limit/cluster-memory-store)
instead.
## Alternate Rate Limiters
This module was designed to only handle the basics and didn't even support
external stores initially. These other options all are excellent pieces of
software and may be more appropriate for some situations:
* [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
* [express-brute](https://www.npmjs.com/package/express-brute)
* [rate-limiter](https://www.npmjs.com/package/express-limiter)
## Issues and Contributing
If you encounter a bug or want to see something added/changed, please go ahead
and
[open an issue](https://github.com/nfriexpress-rate-limitedly/express-rate-limit/issues/new)!
If you need help with something, feel free to
[start a discussion](https://github.com/express-rate-limit/express-rate-limit/discussions/new)!
If you wish to contribute to the library, thanks! First, please read
[the contributing guide](/guides/contributing). Then you can pick up any issue
and fix/implement it!
## License
MIT © [Nathan Friedly](http://nfriedly.com/),
[Vedant K](https://github.com/gamemaker1)
# Installation
Source: https://express-rate-limit.mintlify.app/quickstart/installation
This library can be installed from the
[npm registry](https://npm.im/express-rate-limit), or from
[github releases](https://github.com/express-rate-limit/express-rate-limit/releases).
```sh npm theme={null}
npm install express-rate-limit
```
```sh yarn theme={null}
yarn add express-rate-limit
```
```sh pnpm theme={null}
pnpm add express-rate-limit
```
```sh npm theme={null}
> export VERSION=x.x.x
> export URL="https://github.com/express-rate-limit/express-rate-limit/releases/download/v${VERSION}/express-rate-limit.tgz"
> npm install $URL
```
```sh yarn theme={null}
> export VERSION=x.x.x
> export URL="https://github.com/express-rate-limit/express-rate-limit/releases/download/v${VERSION}/express-rate-limit.tgz"
> yarn add $URL
```
```sh pnpm theme={null}
> export VERSION=x.x.x
> export URL="https://github.com/express-rate-limit/express-rate-limit/releases/download/v${VERSION}/express-rate-limit.tgz"
> pnpm add $URL
```
From `v7.5.0` onwards, the minimum supported version of Express is `v4.11`, due
to the usage of `response.append` in the implementation of the IETF draft
specification for the `RateLimit` headers. If you do not set the
`standardHeaders` option to `draft-7` or `draft-8`, you can still safely use any
version of Express, `v4.0.0` onwards.
You can view the changes made in each version in the
[changelog](../reference/changelog).
# Usage
Source: https://express-rate-limit.mintlify.app/quickstart/usage
### Importing the Library
This library requires [node](https://nodejs.org) version 16 or above.
This library is provided in [esm](https://nodejs.org/api/esm.html) as well as
[cjs](https://nodejs.org/api/modules.html) forms, and works with both Javascript
and Typescript projects.
```ts esm theme={null}
// Your code most likely uses the es module format if the `type` field in your
// `package.json` is set to `module`.
import { rateLimit } from 'express-rate-limit'
```
```ts commonjs theme={null}
// Your code most likely uses the commonjs module format if the `type` field in
// your `package.json` is absent, or set to `commonjs`.
const { rateLimit } = require('express-rate-limit')
```
### Using the Library in Express
The `rateLimit` function accepts an options object and returns the rate limiting
middleware.
An example with the recommended configuration is as follows:
```ts theme={null}
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
ipv6Subnet: 56, // Set to 60 or 64 to be less aggressive, or 52 or 48 to be more aggressive
})
```
Then use it in your [Express](https://expressjs.com/) application as follows:
```ts theme={null}
app.use(limiter)
```
To use it only for a certain path (e.g., limit only calls to the `/auth/*`
endpoints), specify the url as the first parameter in `app.use`:
```ts theme={null}
app.use('/auth', limiter)
```
To use it only for a certain endpoint (e.g., limit calls to
`POST /reset_password`), add the limiter as a middle argument to
`app.get`/`app.post`/etc.:
```ts theme={null}
app.post('/reset_password', limiter, (req, res) => {
// ...
})
```
Take a look at the [configuration page](/reference/configuration) for a list of
options you can use to change the behaviour of the limiter.
If your server runs behind a proxy/load balancer, the IP address of the request
might be `undefined`, or the IP of the load balancer/reverse proxy (leading to
the rate limiter blocking **all** requests once the limit is reached).
To fix this, take a look at the [guide](/guides/troubleshooting-proxy-issues) to
troubleshooting proxy issues.
### Using the library in Next.js
Although not officially supported, several individuals have been able to
successfully use express-rate-limit in [Next.js](https://nextjs.org/) by
defining a custom [`keyGenerator`](/reference/configuration#keygenerator) to
return the user's IP (or some other identifier). However, additional changes are
sometimes needed, such as
[handling schema migrations with rate-limit-postgresql](https://github.com/express-rate-limit/rate-limit-postgresql/issues/36).
### Using External Stores
A store is essentially a javascript/typescript class that allows the library to
store hit counts and reset times for clients wherever you want, e.g., in an
external database.
To use an external store, pass an instance of the store to the `rateLimit`
function, like so:
```ts theme={null}
const limiter = rateLimit({
// ... other options,
store: new ExternalStore(),
})
```
For a list of stores you can use, take a look at the
[data stores page](/reference/stores).
For a tutorial on how to create your own store, see
[this](/guides/creating-a-store).
# Changelog
Source: https://express-rate-limit.mintlify.app/reference/changelog
All notable user-facing changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [8.6.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.6.2)
### Fixed
* `ipKeyGenerator` now detects IPv4-mapped IPv6 addresses by range rather than
by formatting
## [8.6.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.6.1)
### Changed
* Deprecated time constants added in previous release (`DAY`, `HOUR`, `MINUTE`,
and `SECOND`)
* These will be replaced by a slightly different version in the next major
release
## [8.6.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.6.0)
### Fixed
* Resolved an issue where the `used` count could go negative if the
`skipSuccessfulRequests` or `skipFailedRequests` options were used and the
window reset while the request was being handled.
### Added
* Added `DAY`, `HOUR`, `MINUTE`, and `SECOND` constants for use with the
`windowMs` setting.
* Added support for debug logging - see
[https://express-rate-limit.mintlify.app/guides/debugging](https://express-rate-limit.mintlify.app/guides/debugging)
* Validations are now run once each instead of only during the first request.
* This enables the IPv6 checks to run even if the first request happens to be
IPv4.
* It also prevents duplicate messages if the server receives multiple requests
in parallel.
## [8.5.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.5.2)
### Fixed
* Reduced amount of string templating in ipKeyGenerator
## [8.5.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.5.1)
### Fixed
* Updated `ip-address` dependency to latest version due to
[https://github.com/advisories/GHSA-v2v4-37r5-5v8g](https://github.com/advisories/GHSA-v2v4-37r5-5v8g)
## [8.5.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.5.0)
### Added
* Store init functions may now be async / return a promise. If they throw /
reject, the error will be caught and logged.
## [8.4.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.4.1)
### Added
* Custom logger support: new logger option, interface, and default
implementation that maintains the previous console.warn/console.error
behavior.
## [8.4.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.4.0)
### Added
* ~~Custom logger support: new logger option, interface, and default
implementation that maintains the previous console.warn/console.error
behavior.~~
* Due to a mistake in tagging, this was not actually included in the release.
8.4.0 was functionally equivalent to 8.3.2
## [8.3.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.3.2)
### Fixed
* Fixed an issue where skipping failed requests wouldn't work correctly for
requests that were closed very early on
## [8.3.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.3.1)
### Fixed
* Fixed npm provenance on automated releases
* Fixed a broken link in the readme
## [8.3.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.3.0)
> Backported to previous minor versions as
> [8.2.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.2.2),
> [8.1.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.1.1),
> and
> [8.0.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.0.2).
### Security
* Fixed
[ghsa-46wh-pxpv-q5gq](https://github.com/express-rate-limit/express-rate-limit/security/advisories/GHSA-46wh-pxpv-q5gq).
### Provenance note
Due to an issue with automated publishing to npm, we opted to manually publish
v8.3.0 in order to get the fix out quicker. Accitionally, our CI is not
configured to handle backports, so we also manually released those.
## [8.2.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.2.1)
### Fixed
* Don't log ERR\_ERL\_UNKNOWN\_OPTION when used with express-rate-limit
## [8.2.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.2.0)
### Added
* New `knownOptions` validation check, intended to catch typos in configuration,
such as `windowMS` instead of `windowMs`.
## [8.1.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.1.0)
### Fixed
* `RateLimit-Reset` is now always set when `standardHeaders` is set to
`'draft-6'` and the store supports it.
### Added
* New `windowMs` validation check that ensures it's in the valid range when
using the built-in Memory store.
* New `forwardedHeader` validation check to warn when the `Forwarded` header is
present but ignored.
## [8.0.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.0.1)
### Fixed
* `ipKeyGenerator` function is now correctly exported in CommonJS build
* express's `Request` and `Response` types are once again correctly referenced
in .d.ts files
### Changed
* Replaced `ip` library with `ip-address` due to a vulnerability in `ip`.
Note that express-rate-limit did not use the vulnerable code path, but we
swapped the library to prevent users from getting vulnerability warnings and
reports.
## [8.0.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v8.0.0)
### Breaking
* IPv6 addresses are now masked with a /56 subnet by default. For example, the
following two IP addresses will now be considered to be the same user and
grouped together for rate-limiting:
* `0123:4567:89ab:cd11:1111:1111:1111:1111`
* `0123:4567:89ab:cd22:2222:2222:2222:2222`
(both would be normalized to `123:4567:89ab:cd00::/56`)
### Fixed
* Fixed a vulnerability where IPv6 users could bypass rate limiting by iterating
through multiple IP addresses in their ISP-assigned subnet.
### Added
* `ipv6Subnet` configuration option used by the default `keyGenerator`, defaults
to `56`
* `ipKeyGenerator(ip, ipv6Subnet)` helper method to apply the desired subnet to
IPv6 addresses (returns IPv4 unchanged)
* `ipv6Subnet` validation check on above configuration option's value (allowed
range is 32-64)
* `ipv6SubnetOrKeyGenerator` validation check to warn of an incompatible
combination of `ipv6Subnet` and `keyGenerator` settings.
* `keyGeneratorIpFallback` validation check on custom `keyGenerator`s to ensure
they're using `ipKeyGenerator` if they reference `req.ip` or `request.ip`
## [7.5.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.5.1)
### Changed
* Narrowed type of `standardHeaders` from `string` to just the supported values
via a TypeScript
[`const` assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions)
([#506](https://github.com/express-rate-limit/express-rate-limit/pull/506))
## [7.5.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.5.0)
### Added
* Implemented the combined `RateLimit` header according to the eighth draft of
the
[IETF RateLimit header specification](https://github.com/ietf-wg-httpapi/ratelimit-headers).
Enable by setting `standardHeaders: 'draft-8'`.
* Added a new `identifier` option, used as the name for the quota policy in the
`draft-8` headers.
* Added a new `headersDraftVersion` validation check to identifies cases where
an unsupported version string is passed to the `standardHeaders` option.
## [7.4.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.4.1)
### Fixed
* Made the `passOnStoreError` return after calling `next()` rather than
continuing execution.
## [7.4.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.4.0)
### Added
* Added `passOnStoreError` option to allow a way to "fail open" in the event of
a backend error.
## [7.3.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.3.1)
### Fixed
* Changed error displayed for the `creationStack` validation check when a store
with `localKeys` set to false is used.
* Improved documentation for the `creationStack` check.
## [7.3.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.3.0)
### Added
* Added a new `unsharedStore` validation check that identifies cases where a
single store instance is shared across multiple limiters.
## [7.2.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.2.0)
### Added
* Added a new `creationStack` validation check that looks for instances created
in a request handler.
## [7.1.5](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.1.5)
### Fixed
* Enable `async` `requestWasSuccessful` methods to work as documented.
## [7.1.4](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.1.4)
### Fixed
* Ensure header values are strings rather than numbers, for compatibility with
[Bun](https://bun.sh/).
## [7.1.3](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.1.3)
### Changed
* Loosened peer dependencies to explicitly allow the Express 5 beta. (See
[#415](https://github.com/express-rate-limit/express-rate-limit/issues/415))
## [7.1.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.1.2)
### Changed
* Re-organized documentation from readme into docs/ folder and added
documentation website.
## [v7.1.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.1.1)
### Added
* Enabled provenance statement generation, see
[https://github.com/express-rate-limit/express-rate-limit#406](https://github.com/express-rate-limit/express-rate-limit#406).
## [7.1.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.1.0)
### Changed
* The `getKey` method is now always defined. If the store does not have the
required `get` method, `getKey` will throw an error explaining this.
## [7.0.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.0.2)
### Added
* Added `cluster-memory-store` to the readme and made a couple of other minor
clarifications.
## [7.0.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.0.1)
### Added
* Added `rate-limit-postgresql` to the `stores` list in the readme.
## [7.0.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v7.0.0)
### Breaking
* Changed behavior when `max` is set to 0:
* Previously, `max: 0` was treated as a 'disable' flag and would allow all
requests through.
* Starting with v7, all requests will be blocked when max is set to 0.
* To replicate the old behavior, use the
[skip](https://github.com/express-rate-limit/express-rate-limit#skip)
function instead.
* Renamed `req.rateLimit.current` to `req.rateLimit.used`.
* `current` is now a hidden getter that will return the `used` value, but it
will not appear when iterating over the keys or calling `JSON.stringify()`.
* Changed the minimum required Node version from v14 to v16.
* `express-rate-limit` now targets `es2022` in TypeScript/ESBuild.
* Bumped TypeScript from v4 to v5 and `dts-bundle-generator` from v7 to v8.
### Deprecated
* Removed the `draft_polli_ratelimit_headers` option (it was deprecated in v6).
* Use `standardHeaders: 'draft-6'` instead.
* Removed the `onLimitReached` option (it was deprecated in v6).
* [This](\(https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#wrn_erl_deprecated_on_limit_reached\))
is an example of how to replicate it's behavior with a custom `handler`
option.
### Changed
* The `MemoryStore` now uses precise, per-user reset times rather than a global
window that resets all users at once.
* The `limit` configuration option is now preferred to `max`.
* It still shows the same behavior, and `max` is still supported. The change
was made to better align with terminology used in the IETF standard drafts.
### Added
* The `validate` config option can now be an object with keys to enable or
disable specific validation checks. For more information, see
[this](https://github.com/express-rate-limit/express-rate-limit#validate).
## [6.11.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.11.2)
### Fixed
* Restored `IncrementResponse ` TypeScript type (See
[#397](https://github.com/express-rate-limit/express-rate-limit/pull/397))
## [6.11.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.11.1)
### Fixed
* Check for prefixed keys when validating that the stores have single counted
keys (See
[#395](https://github.com/express-rate-limit/express-rate-limit/issues/395)).
## [6.11.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.11.0)
### Added
* Support for retrieving the current hit count and reset time for a given key
from a store (See
[#390](https://github.com/express-rate-limit/express-rate-limit/issues/389)).
## [6.10.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.10.0)
### Added
* Support for combined `RateLimit` header from the
[RateLimit header fields for HTTP standardization draft](https://github.com/ietf-wg-httpapi/ratelimit-headers)
adopted by the IETF. Enable by setting `standardHeaders: 'draft-7'`.
* New `standardHeaders: 'draft-6'` option, treated equivalent to
`standardHeaders: true` from previous releases. Note that `true` and `false`
are still supported.
* New `RateLimit-Policy` header added when `standardHeaders` is set to
`'draft-6'`, `'draft-7'`, or `true`.
* Warning when using deprecated `draft_polli_ratelimit_headers` option.
* Warning when using deprecated `onLimitReached` option.
* Warning when `totalHits` value returned from Store is invalid.
## [6.9.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.9.0)
### Added
* New validaion check for double-counted requests.
* Added help link to each validation error, directing users to the appropriate
wiki page for more info.
### Changed
* Miscellaneous documentation improvements.
## [6.8.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.8.0) & [6.7.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.8.0)
### Changed
* Revert 6.7.1 change that bumped typescript from 5.x to 4.x and
dts-bundle-generator from 8.x to 7.x (See
[#360](https://github.com/express-rate-limit/express-rate-limit/issues/360)).
## [6.8.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.8.0)
### Added
* Added a set of validation checks that will log an error if failed. See
[https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes](https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes) for
a list of potential errors. Can be disabled by setting `validate: false` in
the configuration. Automatically disables after the first request. (See
[#358](https://github.com/express-rate-limit/express-rate-limit/issues/358)).
## [6.7.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.7.1)
### Fixed
* Fixed compatibility with TypeScript's TypeScript new `node16` module
resolution strategy (See
[#355](https://github.com/express-rate-limit/express-rate-limit/issues/355)).
### Changed
* Bumped development dependencies
* This initially include bumping typescript from 4.x to 5.x and
dts-bundle-generator from 7.x to 8.x
* Added `node` 20 to list of versions the CI jobs run on.
No functional changes.
## [6.7.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.7.0)
### Changed
* Updated links to point to the new `express-rate-limit` organization on GitHub.
* Added advertisement to `readme.md` for project sponsor
[Zuplo](https://zuplo.link/express-rate-limit).
* Updated to `typescript` version 5 and bumped other dependencies.
* Dropped `node` 12, and added `node` 19 to the list of versions the CI jobs run
on.
No functional changes.
## [6.6.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.6.0)
### Added
* Added `shutdown` method to the Store interface and the MemoryStore.
## [6.5.2](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.5.2)
### Fixed
* Fixed an issue with missing types in ESM monorepos.
## [6.5.1](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.5.1)
### Added
* The message option can now be a (sync/asynx) function that returns a value
(#311)
### Changed
* Updated all dependencies
Note: 6.5.0 was not released due to CI automation issues.
## [6.4.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.3.0)
### Added
* Adds Express 5 (`5.0.0-beta.1`) as a supported peer dependency (#304)
### Changed
* Tests are now run on Node 12, 14, 16 and 18 on CI (#305)
* Updated all development dependencies (#306)
## [6.3.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.3.0)
### Changed
* Changes the build target to es2019 so that ESBuild outputs code that can run
with Node 12.
* Changes the minimum required Node version to 12.9.0.
## [6.2.1](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.2.1)
### Fixed
* Use the default value for an option when `undefined` is passed to the rate
limiter.
## [6.2.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.2.0)
### Added
* Export the `MemoryStore`, so it can now be imported as a named import
(`import { MemoryStore } from 'express-rate-limit'`).
### Fixed
* Deprecate the `onLimitReached` option (this was supposed to be deprecated in
v6.0.0 itself); developers should use a custom handler function that checks if
the rate limit has been exceeded instead.
## [6.1.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.1.0)
### Added
* Added a named export `rateLimit` in case the default import does not work.
### Fixed
* Added a named export `default`, so Typescript CommonJS developers can
default-import the library (`import rateLimit from 'express-rate-limit'`).
## [6.0.5](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.0.5)
### Fixed
* Use named imports for ExpressJS types so users do not need to enable the
`esModuleInterop` flag in their Typescript compiler configuration.
## [6.0.4](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.0.4)
### Fixed
* Upload the built package as a `.tgz` to GitHub releases.
### Changed
* Add ` main` and `module` fields to `package.json`. This helps tools such as
ESLint that do not yet support the `exports` field.
* Bumped the minimum node.js version in `package-lock.json` to match
`package.json`
## [6.0.3](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.0.3)
### Changed
* Bumped minimum Node version from 12.9 to 14.5 in `package.json` because the
transpiled output uses the nullish coalescing operator (`??`), which
[isn't supported in node.js prior to 14.x](https://node.green/#ES2020-features--nullish-coalescing-operator-----).
## [6.0.2](https://github.com/nfriedly/express-rate-limit/releases/v6.0.2)
### Fixed
* Ensure CommonJS projects can import the module.
### Added
* Add additional tests that test:
* importing the library in `js-cjs`, `js-esm`, `ts-cjs`, `ts-esm`
environments.
* usage of the library with external stores (`redis`, `mongo`, `memcached`,
`precise`).
### Changed
* Use [`esbuild`](https://esbuild.github.io/) to generate ESM and CJS output.
This reduces the size of the built package from 138 kb to 13kb and build time
to 4 ms! :rocket:
* Use [`dts-bundle-generator`](https://github.com/timocov/dts-bundle-generator)
to generate a single Typescript declaration file.
## [6.0.1](https://github.com/nfriedly/express-rate-limit/releases/v6.0.1)
### Fixed
* Ensure CommonJS projects can import the module.
## [6.0.0](https://github.com/nfriedly/express-rate-limit/releases/v6.0.0)
### Added
* `express` 4.x as a peer dependency.
* Better Typescript support (the library was rewritten in Typescript).
* Export the package as both ESM and CJS.
* Publish the built package (`.tgz` file) on GitHub releases as well as the npm
registry.
* Issue and PR templates.
* A contributing guide.
### Changed
* Rename the `draft_polli_ratelimit_headers` option to `standardHeaders`.
* Rename the `headers` option to `legacyHeaders`.
* `Retry-After` header is now sent if either `legacyHeaders` or
`standardHeaders` is set.
* Allow `keyGenerator` to be an async function/return a promise.
* Change the way custom stores are defined.
* Add the `init` method for stores to set themselves up using options passed
to the middleware.
* Rename the `incr` method to `increment`.
* Allow the `increment`, `decrement`, `resetKey` and `resetAll` methods to
return a promise.
* Old stores will automatically be promisified and used.
* The package can now only be used with NodeJS version 12.9.0 or greater.
* The `onLimitReached` configuration option is now deprecated. Replace it with a
custom `handler` that checks the number of hits.
### Removed
* Remove the deprecated `limiter.resetIp` method (use the `limiter.resetKey`
method instead).
* Remove the deprecated options `delayMs`, `delayAfter` (the delay functionality
was moved to the
[`express-slow-down`](https://github.com/nfriedly/express-slow-down) package)
and `global` (use a key generator that returns a constant value).
## [5.x](https://github.com/nfriedly/express-rate-limit/releases/tag/v5.5.1)
### Added
* The middleware ~~throws~~ logs an error if `request.ip` is undefined.
### Removed
* Removes typescript typings. (See
[#138](https://github.com/nfriedly/express-rate-limit/issues/138))
## [4.x](https://github.com/nfriedly/express-rate-limit/releases/tag/v4.0.4)
### Changed
* The library no longer modifies the passed-in options object, it instead makes
a clone of it.
## [3.x](https://github.com/nfriedly/express-rate-limit/releases/tag/v3.5.2)
### Added
* Simplifies the default `handler` function so that it no longer changes the
response format. The default handler also uses
[response.send](https://expressjs.com/en/4x/api.html#response.send).
### Changes
* `onLimitReached` now only triggers once for a client and window. However, the
`handle` method is called for every blocked request.
### Removed
* The `delayAfter` and `delayMs` options; they were moved to the
[express-slow-down](https://npmjs.org/package/express-slow-down) package.
## [2.x](https://github.com/nfriedly/express-rate-limit/releases/tag/v2.14.2)
### Added
* Support external stores (from version 2.3.0) onwards.
* A `limiter.resetKey()` method to reset the hit counter for a particular client
### Changes
* The rate limiter now uses a less precise but less resource intensive method of
tracking hits from a client.
### Removed
* The `global` option.
# Configuration
Source: https://express-rate-limit.mintlify.app/reference/configuration
No configuration is required, all options have reasonable defaults.
Any option that may be set to a function may also be set to an async function,
or to a function that returns a promise. Async functions and promises will be
awaited for the value.
## `windowMs`
> `number`
Time frame for which requests are checked/remembered. The time window for each
user starts at their first request. After it elapses, the user's hit count is
reset to zero. A new time window is then started on their next request.
Also used in the `Retry-After` header when the limit is reached.
This value **cannot** be set to a function. If multiple time windows are needed,
create multiple rate limiters.
For stores that do not implement the `init` function (including all legacy
stores - see the [data stores page][stores]), you may need to configure this
value twice, once here and once on the store. In some cases the units also
differ (e.g. seconds vs milliseconds).
Maximum value with the default MemoryStore is `2147483647` (2^31-1, \~28.4 days)
due to
[limitations of the `setInterval()` call in node.js](https://nodejs.org/api/timers.html#setintervalcallback-delay-args).
(Also, all rate limits in the default MemoryStore are reset when the node.js
process is restarted.) Most other [stores] support larger values and retain
their state when node.js is reset.
Defaults to `60000` ms (= 1 minute).
### Time constants
Constants for `DAY`, `HOUR`, `MINUTE`, and `SECOND` are provided to make the
`windowMs` configuration a little easier to work with.
For example, to set a 3 hour window:
```ts theme={null}
import { rateLimit, HOUR } from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 3 * HOUR,
// ...
})
```
Or, for a 1 minute and 30 second window:
```ts theme={null}
import { rateLimit, MINUTE, SECOND } from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 1 * MINUTE + 30 * SECOND,
// ...
})
```
## `limit`
> `number | function`
>
> Renamed in v7.x from `max` to `limit`. However, `max` will still be supported
> for backwards-compatibility.
The maximum number of connections to allow during the `window` before rate
limiting the client.
Can be the limit itself as a number or a (sync/async) function that accepts the
Express `req` and `res` objects and then returns a number.
As of version 7.0.0, setting `max` to zero will no longer disable the rate
limiter - instead, it will 'block' all requests to that endpoint.
Defaults to `5`.
An example of using a function:
```ts theme={null}
const isPremium = async (user) => {
// ...
}
const limiter = rateLimit({
// ...
limit: async (req, res) => {
if (await isPremium(req.user)) return 10
else return 5
},
})
```
## `message`
> `any`
The response body to send back when a client is rate limited.
May be a string, JSON object, or any other value that Express's
[res.send](https://expressjs.com/en/4x/api.html#res.send) method supports. It
can also be a (sync/async) function that accepts the Express request and
response objects and then returns a `string`, JSON object, etc.
Defaults to `'Too many requests, please try again later.'`
An example of sending a JSON object:
```js theme={null}
app.use(
rateLimit({
message: { error: 'Too many requests, please try again later.' },
// ...
}),
)
```
An example of using a function:
```ts theme={null}
const isPremium = async (user) => {
// ...
}
const limiter = rateLimit({
// ...
message: async (req, res) => {
if (await isPremium(req.user))
return 'You can only make 10 requests every hour.'
else return 'You can only make 5 requests every hour.'
},
})
```
Set a custom [`handler`](#handler) for more advanced use-cases, such as using
[`res.render()`](http://expressjs.com/en/4x/api.html#res.render) to send a
templated response.
## `statusCode`
> `number`
The HTTP status code to send back when a client is rate limited.
Defaults to `429`.
The HTTP status code `429` is defined in
[section 4](https://datatracker.ietf.org/doc/html/rfc6585#section-4) of IETF's
RFC 6585.
## `handler`
> `function`
Express request handler that sends back a response when a client is
rate-limited.
By default, sends back the `statusCode` and `message` set via the `options`,
similar to this:
```ts theme={null}
const limiter = rateLimit({
// ...
handler: (req, res, next, options) =>
res.status(options.statusCode).send(options.message),
})
```
## `legacyHeaders`
> `boolean`
>
> Renamed in `6.x` from `headers` to `legacyHeaders`.
Whether to send the legacy rate limit headers for the limit
(`X-RateLimit-Limit`), current usage (`X-RateLimit-Remaining`) and reset time
(if the store provides it) (`X-RateLimit-Reset`) on all responses. If set to
`true`, the middleware also sends the `Retry-After` header on all blocked
requests.
Defaults to `true`.
Note that this option only defaults to `true` for backward compatibility, and it
is recommended to set it to `false` and use `standardHeaders` instead.
## `standardHeaders`
> `boolean` | `'draft-6'` | `'draft-7'` | `'draft-8'`
>
> Renamed in `6.x` from `draft_polli_ratelimit_headers` to `standardHeaders`.
Whether to enable support for headers conforming to the
[RateLimit header fields for HTTP standardization draft](https://github.com/ietf-wg-httpapi/ratelimit-headers)
adopted by the IETF.
If set to `draft-6`, separate `RateLimit-Policy` `RateLimit-Limit`,
`RateLimit-Remaining`, and, if the store supports it, `RateLimit-Reset` headers
are set on the response, in accordance with the
[sixth draft](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-06)
of the IETF rate limit header specification.
If set to `draft-7`, a combined `RateLimit` header is set containing limit,
remaining, and reset values, and a `RateLimit-Policy` header is set, in
accordance with the
[seventh draft](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-07)
of the IETF rate limit header specification. `windowMs` is used for the reset
value if the store does not provide a reset time.
If set to `draft-8`, a combined `RateLimit` header is set containing the
remaining and reset values, along with a `RateLimit-Policy` header, is set for
each rate limiter defined, in accordance with the
[eighth draft](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-08)
of the IETF rate limit header specification. The value of `windowMs` is used for
the reset parameter if the store does not provide a reset time.
Note that the
[ninth draft](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-09)
of the IETF rate limit header specification does not specify any changes in the
function or format of the headers. Set this option to `draft-8` for the same.
If set to generate `draft-7` or `draft-8` headers, make sure that you are using
Express `v4.11` or later, as the implementation of these two drafts requires the
`response.append` function.
If set to `true`, it is treated as `draft-6`, however, this behavior may change
in a future semver major release.
If set to any truthy value, the middleware also sends the `Retry-After` header
on all blocked requests.
The `standardHeaders` option may be used in conjunction with, or instead of the
`legacyHeaders` option.
Tip: use the
[ratelimit-header-parser](https://www.npmjs.com/package/ratelimit-header-parser)
library in clients to read/parse any form of headers.
Defaults to `false`.
## `identifier`
> `string | function`
The name associated with the quota policy that this instance of the rate limiter
enforces. It is used in the `RateLimit` and `RateLimit-Policy` headers when
`standardHeaders` is set to `draft-8`, and ignored otherwise.
Can be the identifier itself as a string or a (sync/async) function that accepts
the Express `req` and `res` objects and then returns a string.
Defaults to a function that returns a string of the format
`{limit}-in-{window}`, e.g., `7-in-1.5sec`.
## `store`
> `Store`
The `Store` to use to store the hit count for each client.
By default, the
[`memory-store`](https://github.com/express-rate-limit/express-rate-limit/blob/main/source/memory-store.ts)
is used.
See [Data Stores](/reference/stores) for a list of avalliable stores and other
information.
## `passOnStoreError`
By default, this is `false` and express-rate-limit will call the
[express error handler](http://expressjs.com/en/guide/error-handling.html#error-handling)
in the event of a store error. In other words, the default is to "fail closed"
and block all requests if the datastore becomes unavailable.
Set this to `true` to "fail open" allow the request(s) through without rate
limiting. The error will be logged in this case.
## `keyGenerator`
> `function`
Method to retrieve custom identifiers for clients, such as their IP address,
username, or API Key.
Should be a (sync/async) function that accepts the Express `request` and
`response` objects and then returns a string.
By default, the client's IP address is used with the
[`ipKeyGenerator`](./helpers#ipkeygenerator) method to apply the configured
[`ipv6Subnet`](#ipv6subnet) to IPv6 addresses.
Custom `keyGenerator`s that fall back to IP addresses should use the provided
[`ipKeyGenerator`](./helpers#ipkeygenerator) method and a chosen subnet for IPv6
addresses (see [ipv6Subnet](#ipv6subnet) discussion below.)
```ts theme={null}
import { rateLimit, ipKeyGenerator } from 'express-rate-limit'
const limiter = rateLimit({
// ...
keyGenerator: (req, res) => ipKeyGenerator(req.ip),
})
```
If a `keyGenerator` returns the same value for every user, it becomes a global
rate limiter. This could be combined with a second instance of
`express-rate-limit` to have both global and per-user limits.
## `ipv6Subnet`
> `number` (32-64) | `function` | `false`
IPv6 subnet mask applied to IPv6 addresses in the default
[`keyGenerator`](#keygenerator).
Generally, ISPs that support IPv6 give each of their customers a range of IPv6
addresses via a subnet mask, whereas they usually provide only a single IPv4
address per customer. A malicious user could iterate through their range of IPv6
addresses and bypass simple IP-based rate limiting. This setting counteracts
that by allowing the rate-limiter to block an entire range of IPv6 addresses at
once.
It's generally not possible to know what subnet an ISP has assigned to a user,
so we have to make an educuated guess.
The default value is 56, corresponding to a /56 subnet.
The valid range is 1-128, but typically ISPs assign subnets in the range of
32-64, with 64 being the most common. The validation check (which can be
disabled) will warn for values outside the 32-64 range.
Smaller values block larger ranges of IPs at once. 56 is a moderately aggressive
default. It may be increased to if users are being incorrectly blocked (try 60
or 64), or decreased if you are seeing evidence of abuse.
64, 60 ([Comcast](https://news.ycombinator.com/item?id=44228908)), 56, and 48
are all common values used by various ISPs.
The option may also be set to a function that returns the value if you want to
apply different subnets to different users. (It's not always possible to know
what subnet a given user has, but you could make an educuated guess based on
their ISP - see the example below.)
Set to false to disable and always use the IP without masking.
This example uses the node.js built-in
[`net.BlockList`](https://nodejs.org/api/net.html#class-netblocklist) to apply
different subnets to different IP ranges:
```js theme={null}
import { BlockList } from 'node:net'
// or
// const { BlockList } = require('node:net')
// Comcast defaults to /64 but allows customers to request /60
// (note that this is a non-exhaustive list, there are many more)
const networks60 = new BlockList()
networks60.addSubnet('2a0c:93c0:10::', 44, 'ipv6')
networks60.addSubnet('2a0c:93c0:6000::', 48, 'ipv6')
networks60.addSubnet('2a0c:93c0:6002::', 48, 'ipv6')
// AWS allows customers to request up to a /44!
// (the full list is at https://ip-ranges.amazonaws.com/ip-ranges.json)
const networks44 = new BlockList()
networks44.addSubnet('2600:1f69:7400::', 40, 'ipv6')
app.use(
rateLimit({
ipv6Subnet: function (req, res) {
if (networks60.check(req.ip, 'ipv6')) return 60
if (networks44.check(req.ip, 'ipv6')) return 44
return 56 // fallback
},
// ...
}),
)
```
## `requestPropertyName`
> `string`
The name of the property on the Express `request` object to store the rate limit
info.
Defaults to `'rateLimit'`.
## `skip`
> `function`
Function to determine whether or not this request counts towards a client's
quota. Should be a (sync/async) function that accepts the Express `request` and
`response` objects and then returns `true` or `false`.
Could also act as an allowlist for certain keys:
```ts theme={null}
const allowlist = ['192.168.0.56', '192.168.0.21']
const limiter = rateLimit({
// ...
skip: (req, res) => allowlist.includes(req.ip),
})
```
By default, it skips no requests:
```ts theme={null}
const limiter = rateLimit({
// ...
skip: (req, res) => false,
})
```
## `skipSuccessfulRequests`
> `boolean`
If `true`, the library will (by default) skip all requests that are considered
'failed' by the `requestWasSuccessful` function. By default, this means requests
succeed when the response status code less than `400`.
Technically, the requests are counted and then un-counted, so a large number of
slow requests all at once could still trigger a rate-limit. This may be fixed in
a future release. PRs welcome!
Defaults to `false`.
## `skipFailedRequests`
> `boolean`
When set to `true`, failed requests won't be counted. Request considered failed
when the `requestWasSuccessful` option returns `false`. By default, this means
requests fail when:
* the response status >= `400`
* the request was cancelled before last chunk of data was sent (response `close`
event triggered)
* the response `error` event was triggered by response
Technically, the requests are counted and then un-counted, so a large number of
slow requests all at once could still trigger a rate-limit. This may be fixed in
a future release. PRs welcome!
Defaults to `false`.
## `requestWasSuccessful`
> `function`
Method to determine whether or not the request counts as 'successful'. Used when
either `skipSuccessfulRequests` or `skipFailedRequests` is set to true. Should
be a (sync/async) function that accepts the Express `req` and `res` objects and
then returns `true` or `false`.
By default, requests with a response status code less than `400` are considered
successful:
```ts theme={null}
const limiter = rateLimit({
// ...
requestWasSuccessful: (req, res) => res.statusCode < 400,
})
```
## `validate`
> `boolean | Object`
When enabled, a set of validation checks are run early on to detect common
misconfigurations with proxies, etc. Prints an error to the console if any issue
is detected.
If set to `true` or `false`, all validations are enabled or disabled.
If set to an object, individual validations can be enabled or disabled by name,
and the key `default` controls all unspecified validations. For example:
```js theme={null}
const limiter = rateLimit({
validate: {
xForwardedForHeader: false,
default: true,
},
// ...
})
```
Supported validations are:
* [ip](/reference/error-codes#err-erl-undefined-ip-address)
* [trustProxy](/reference/error-codes#err-erl-permissive-trust-proxy)
* [xForwardedForHeader](/reference/error-codes#err-erl-unexpected-x-forwarded-for)
* [forwardedHeader](/reference/error-codes#err-erl-forwarded-header)
* [positiveHits](/reference/error-codes#err-erl-invalid-hits)
* [unsharedStore](/reference/error-codes#err-erl-store-reuse)
* [singleCount](/reference/error-codes#err-erl-double-count)
* [limit](/reference/error-codes#wrn-erl-max-zero)
* [draftPolliHeaders](/reference/error-codes#wrn-erl-deprecated-draft-polli-headers)
* [onLimitReached](/reference/error-codes#wrn-erl-deprecated-on-limit-reached)
* [headersDraftVersion](/reference/error-codes#err-erl-headers-unsupported-draft-version)
* [headersResetTime](/reference/error-codes#err-erl-headers-no-reset)
* [creationStack](/reference/error-codes#err-erl-created-in-request-handler)
* [knownOptions](/reference/error-codes)
* [validationsConfig](/reference/error-codes#err-erl-unknown-validation)
* [ipv6Subnet](/reference/error-codes#err-erl-ipv6-subnet)
* [ipv6SubnetOrKeyGenerator](/reference/error-codes#err-erl-ipv6subnet-or-keygenerator)
* [keyGeneratorIpFallback](/reference/error-codes#err-erl-key-gen-ipv6)
* [windowMs](/reference/error-codes#err-erl-window-ms)
Defaults to `true`.
[stores]: /reference/stores
## `logger`
> `Logger`
The Logger instance to use to log validation errors or warnings. Default
behavior is to use the console. The first argument is the error that was raised,
the second is an optional message explaining the error. For example:
```js theme={null}
const limiter = rateLimit({
logger: {
warn: (error, message) => {
// TODO: do something with error and message
},
error: (error, message) => {
// TODO: do something with error and message
},
},
})
```
## Debug logging
Debug logging is controlled via the `DEBUG` environment property rather than a
configuration option. See [Debugging Express Rate Limit](/guides/debugging) for
additional details.
# Error Codes
Source: https://express-rate-limit.mintlify.app/reference/error-codes
The library runs a number of validation checks to look for common configuration
issues. If a validation check fails, an error is logged to the console.
Note that validation checks may be disabled globally by passing
`validate: false` in the options, or disabled individually with the settings
documented below. Additionally, specific checks will be disabled in some
circumstances, also discussed below. Otherwise, each check will run once then
disable itself.
The ability to disable individual checks was added in version `7.0.0`.
## Errors
### `ERR_ERL_UNDEFINED_IP_ADDRESS`
> Added in `6.8.0`.
This error is logged whenever `request.ip` is undefined. It could indicate a
misconfiguration in server settings, or that
[the client disconnected](https://nodejs.org/api/net.html#net_socket_remoteaddress)
before the request could be processed.
It could also indicate that a server other than [Express](http://expressjs.com/)
is being used, which is not a supported use-case.
This check will be prevented if a custom `keyGenerator` is supplied.
Set `validate: {ip: false}` in the options to disable the check.
### `ERR_ERL_INVALID_IP_ADDRESS`
> Added in `6.8.0`.
This error is logged whenever the IP address is not a valid IPV4/IPV6 address,
or when the IP address contains a port number. The port from which a request
comes can be changed simply by opening or closing a browser, or when using an
Azure proxy, thus opening up avenues for bypassing the rate limit.
Consider using a custom `keyGenerator` function that strips out the port number
(`request.ip.replace(/:\d+[^:]*$/, '')`, or that uses something like an API key
or bearer token to count the hits against.
See [#234](https://github.com/express-rate-limit/express-rate-limit/issues/234)
for more information on this issue.
This check will be prevented if a custom `keyGenerator` is supplied.
Set `validate: {ip: false}` in the options to disable the check.
### `ERR_ERL_PERMISSIVE_TRUST_PROXY`
> Added in `6.8.0`.
This error is logged when the `trust proxy` setting is set to `true`.
If this is set to true, it will cause express to return the leftmost entry in
the `X-Forwarded-For` header as the client's IP. This header could be set by the
proxy or a malicious client, opening up avenues for bypassing the rate limiter.
Refer to the
[troubleshooting proxy issues](/guides/troubleshooting-proxy-issues) page for a
guide to set the `trust proxy` value correctly.
This check will be prevented if a custom `keyGenerator` is supplied.
Set `validate: {trustProxy: false}` in the options to disable the check.
### `ERR_ERL_UNEXPECTED_X_FORWARDED_FOR`
> Added in `6.8.0`.
This error is logged when the `X-Forwarded-For` header is set (indicating use of
a proxy), but the `trust proxy` setting is `false` (which is the default value).
This usually indicates a configuration issue that will cause express-rate-limit
to apply it's limits global rather than on a per-user basis. Refer to the
[troubleshooting proxy issues](/guides/troubleshooting-proxy-issues) page for a
guide to set the `trust proxy` value correctly.
If this error occurs only rarely, and you do not have a reverse proxy, it may
indicate a malicious user probing for vulnerabilities.
This check will be prevented if a custom `keyGenerator` is supplied.
Set `validate: {xForwardedForHeader: false}` in the options to disable the
check.
### `ERR_ERL_FORWARDED_HEADER`
> Added in `8.1.0`.
This error is logged when the `Forwarded` header
([RFC 7239](https://tools.ietf.org/html/rfc7239)) is set (indicating use of a
proxy) and the default [`keyGenerator`] is used.
As of express\@5.1.0, express's `trust proxy` setting does not support the
`Forwarded` header, only `X-Forwarded-For`, and express-rate-limit relies on
express to determine the requester's IP address, so this header is ignored in
the default configuration.
(There is an
[open ticket requesting support be added to the proxy-addr library](https://github.com/jshttp/proxy-addr/issues/20)
that express uses to determine IP address.)
To use this header, add a custom [keyGenerator] that parses it and returns the
correct IP. For example, using the
[forwarded-parse](https://www.npmjs.com/package/forwarded-parse) library:
```js theme={null}
import parseForwarded from 'forwarded-parse'
import { rateLimit, ipKeyGenerator } from 'express-rate-limit'
// ...
const NUMBER_OF_PROXIES_TO_TRUST = 1
app.use(
rateLimit({
keyGenerator: (req, res) => {
let ip = req.ip
try {
const forwards = parseForwarded(req.headers.forwarded)
ip = forwards[forwards.length - NUMBER_OF_PROXIES_TO_TRUST].for
} catch (ex) {
console.error(
`Error parsing Forwarded header ${req.headers.forwarded} from ${req.ip}:`,
ex,
)
}
return ipKeyGenerator(ip)
},
// ...
}),
)
```
(`ipKeyGenerator` is needed for proper limiting of IPv6 users, see
[ERR\_ERL\_KEY\_GEN\_IPV6](#err-erl-ipv6subnet-or-keygenerator) for more info.)
If there are multiple proxies between your server and the internet and each adds
a `Forwarded` header, increment `NUMBER_OF_PROXIES_TO_TRUST` to match.
See [Troubleshooting Proxy Issues](/guides/troubleshooting-proxy-issues) for
additional information.
`req.ip` could also be set before express-rate-limit processes the request. If
it is set to anything other than the default value (`req.socket.remoteAddress`),
this error will be automatically suppressed.
Set `validate: {forwardedHeader: false}` in the options to disable the check.
### `ERR_ERL_INVALID_HITS`
> Added in `6.10.0`.
This indicates an issue with the Store and/or it's underlying data storage
mechanism.
Ensure the Store returns a positive integer for the `totalHits` value when
`increment()` is called. If this is not possible, the store should throw an
error.
Set `validate: {positiveHits: false}` in the options to disable the check.
### `ERR_ERL_STORE_REUSE`
> Added in `7.3.0`.
This indicates that the single [Store](./stores) instance was used in more than
one rate limiter. This can lead to problems such as initialization logic running
multiple times, and inconsistent reset times.
Instead, create a new store instance for each rate limiter. (Generally with a
unique `prefix` value for each one.)
Set `validate: {unsharedStore: false}` in the options to disable the check.
### `ERR_ERL_DOUBLE_COUNT`
> Added in `6.9.0`.
This indicates that the hit count for a given IP or key was incremented more
than once for a single request. It could happen if the same instance of
express-rate-limit is called more than once, or if multiple instances are called
that use the same [Store](/reference/stores).
* If only a single rate limit is desired, find and remove the extra rateLimit
call(s).
* If multiple rate limits are desired, consider one of the following options:
* Set a custom `prefix` in the configuration for each store instance.
([Redis](https://www.npmjs.com/package/rate-limit-redis#prefix),
[Memcached](https://www.npmjs.com/package/rate-limit-memcached#prefix),
[PostgreSQL](https://www.npmjs.com/package/@acpr/rate-limit-postgresql#constructor),
[Cluster](https://www.npmjs.com/package/@express-rate-limit/cluster-memory-store#prefix))
* Set a custom [`keyGenerator`](/reference/configuration#keygenerator) in the
configuration for each express-rate-limit instance.
In rare circumstances this error can be a false positive. This would include
situations where multiple rate limiters are configured to use the same key but
different backed databases. Setting a unique key per rate limiter would prevent
this.
Prior to version `6.11.1`, the library did not take a store prefixes into
account, and thus could log this incorrectly if two instances use different
prefixes.
Set `validate: {singleCount: false}` in the options to disable the check.
### `ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION`
> Added in `7.5.0`
The library only implements the
[sixth](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-06),
[seventh](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-07)
and
[eighth](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-08)
drafts of the IETF RateLimit headers specification. If draft version other than
`draft-6`, `draft-7`, or `draft-8` is passed to the `standardHeaders` option,
this error is raised.
### `ERR_ERL_HEADERS_NO_RESET`
> Added in `7.0.0`.
The
[seventh draft](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers-07)
of the IETF RateLimit headers specification makes `reset` a required field.
However, the legacy `Store` API did not have a way for stores to provide this
information.
The library attempts to account for this by using the `windowMs` time, which has
a default value of 60 seconds if not explicitly set.
To resolve this, either change to a different headers version, such as
`draft-6`, or a different [store](/reference/stores).
Set `validate: {headersResetTime: false}` in the options to disable the check.
### `ERR_ERL_UNKNOWN_OPTION`
> Added in `8.2.0`
Indicates that one or more of the configuration options passed to
express-rate-limit is unrecognized.
This could be due to a typo or deprecated option, or if extra values were
intentionally added to the configuration object.
See [Configuration](/reference/configuration) for the complete list of valid
configuration options.
Set `validate: {knownOptions: false}` in the options to disable the check.
### `ERR_ERL_UNKNOWN_VALIDATION`
> Added in `7.0.0`.
The library allows for specific validation checks to be enabled or disabled, but
only ones that it knows about. If an unknown validation check is referenced in
the `validate` configuration object, this error will be logged.
This could be due to a typo in the `validate` configuration, or the
configuration could be intended for a different version of the library that
includes the unrecognized validation check.
To resolve this, remove the specified key from your configuration.
To prevent the validation check that logs this error, set the
`validate.validationsConfig` option to `false`:
```js theme={null}
const limiter = rateLimit({
validate: {
validationsConfig: false,
// ...
default: true,
},
// ...
})
```
Set `validate: {validationsConfig: false}` in the options to disable the check.
### `ERR_ERL_CREATED_IN_REQUEST_HANDLER`
Instances of express-rate-limit should be created before a request comes in, not
in response to one.
Incorrect example:
```js theme={null}
import { rateLimit } from 'express-rate-limit'
app.use((req, res, next) => {
// This won't work!
rateLimit({/*...*/})
next()
})
```
Correct example:
```js theme={null}
import { rateLimit } from 'express-rate-limit'
app.use(rateLimit({/*...*/});
```
To only apply the rate limit to some requests, use the
[`skip`](/reference/configuration#skip),
[`skipSuccessfulRequests`](/reference/configuration#skipsuccessfulrequests), or
[`skipFailedRequests`](/reference/configuration#skipfailedrequests) options.
Example where the rate limit only applies to users who are not logged in:
```js theme={null}
// first determine the user's logged-in status
app.use((req, res, next) => {
if (/* user is logged in */) {
req.isLoggedIn = true
} else {
req.isLoggedIn = false
}
next()
})
// next configure express-rate-limit to skip logged in users
app.use(rateLimit({
skip: (req, res) => req.isLoggedIn,
// ...
}))
```
If using a factory function to create express-rate-limit instances, ensure that
it is called at app initialization, not in response to each request:
```diff theme={null}
function rateLimitFactory() {
return rateLimit({/*...*/});
}
- app.use(rateLimitFactory); // broken
+ app.use(rateLimitFactory()); // works
```
In certain advanced use cases, such as serving multiple websites from a single
app, it may be necessary to dynamically create a rate limiter in response to a
request. In this case the validation check may be disabled, but care should be
taken to ensure everything works as expected. Consider caching the limiter after
it's first usage to avoid repeating the initialization cost and any background
work the store may require.
Set `validate: {creationStack: false}` in the options to disable the check.
### `ERR_ERL_IPV6_SUBNET`
> Added in `8.0.0`.
The [`ipv6Subnet`](./configuration#ipv6subnet) option must be set to an integer
in the range of 1 to 128 (inclusive), or a function that returns such an
integer. Set it to `false` to disable the subnet masking.
*However* this validation check is more restrictive in that it only allows for a
number in the more common range of 32 to 64. Subnet values less than /32 will
block a large portion of the internet, and on the other side, ISPs very rarely
give out subnets larger than /64 because certain IPv6 features (SLAAC) depend on
it. Disable this check if you are confident that larger or smaller subnets are
correct for your use-case.
Set `validate: {ipv6Subnet: false}` in the options to disable the check.
### `ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR`
> Added in `8.0.0`.
The [`ipv6Subnet`](./configuration#ipv6subnet) and
[`keyGenerator`](./configuration#keygenerator) options are mutually exclusive. A
custom `keyGenerator` function does respect the value of the `ipv6Subnet`
option - only the default `keyGenerator` makes use of it.
When using a custom `keyGenerator`, the `ipv6Subnet` should instead be set or
calculated inside the `keyGenerator` and passed as a second parameter to the
[`ipKeyGenerator`](./helpers#ipkeygenerator) helper function like so:
```js theme={null}
import { rateLimit, ipKeyGenerator } from 'express-rate-limit'
const limiter = rateLimit({
// ...
keyGenerator: (req, res) => {
if (req.query.apiKey) return req.query.apiKey
const ipv6Subnet = 64 // calculate or set a fixed value here
return ipKeyGenerator(req.ip, ipv6Subnet)
},
})
```
Set `validate: {ipv6SubnetOrKeyGenerator: false}` in the options to disable the
check.
### `ERR_ERL_KEY_GEN_IPV6`
> Added in `8.0.0`.
This error indicates that your custom
[`keyGenerator`](./configuration#keygenerator) appears to have a vulnerability
where IPv6 users can easily bypass rate-limiting by rotating through their
available IP addresses. (See [`ipv6Subnet`](./configuration#ipv6subnet) setting
for more info.)
Correct this error by wrapping your use of `req.ip` with the
[`ipKeyGenerator`](./helpers#ipkeygenerator) helper function like so:
```js theme={null}
import { rateLimit, ipKeyGenerator } from 'express-rate-limit
const limiter = rateLimit({
// ...
keyGenerator: (req, res) => {
// Use API key (or some other identifier) for authenticated users
if (req.query.apiKey) return req.query.apiKey
// fallback to IP for unauthenticated users
// return req.ip // vulnerable
return ipKeyGenerator(req.ip) // better
}
})
```
(See the [example above](#err-erl-ipv6subnet-or-keygenerator) for a custom
`ipv6Subnet`.)
Disable this check if your `keyGenerator` already takes IPv6 subnets into
account.
Set `validate: {keyGeneratorIpFallback: false}` in the options to disable the
check.
### `ERR_ERL_WINDOW_MS`
> Added in `8.1.0`
Node.js (as of version 24.6.0)
[limits `setInterval()` delays to the max 32-bit signed integer value of `2147483647` milliseconds](https://nodejs.org/api/timers.html#setintervalcallback-delay-args),
which is about 28.4 days. Accordingly, express-rate-limit checks the `windowMs`
value before using it in the default MemoryStore and warns when it's out of
range.
Longer values can be used with most external [stores](/reference/stores).
Set `validate: {windowMs: false}` in the options to disable the check.
## Warnings
### `WRN_ERL_MAX_ZERO`
> Added in `6.10.0`.
In express-rate-limit version 6 and older, the rate limiter would be disabled
when setting `limit` (previously `max` until version `7.0.0`) to `0` in the
options. Starting with version `7.0.0`, this is no longer the case - the rate
limiting will apply from the very first request instead. See
[#369](https://github.com/express-rate-limit/express-rate-limit/discussions/369)
for more information.
To recreate the original behavior of disabling the rate limiter entirely, use
the [skip](https://github.com/express-rate-limit/express-rate-limit#skip)
function instead.
Set `validate: {limit: false}` in the options to disable the check.
### `WRN_ERL_DEPRECATED_ON_LIMIT_REACHED`
> Added in `6.10.0`.
The `onLimitReached` configuration option was deprecated in express-rate-limit
v6 and removed in version `7.0.0`. To replicate it's behavior, set a custom
handler like so:
```js theme={null}
const limiter = rateLimit({
// ...
handler: (request, response, next, options) => {
if (request.rateLimit.used === request.rateLimit.limit + 1) {
// onLimitReached code here
}
response.status(options.statusCode).send(options.message)
},
})
```
([Alternatives for `express-slow-down` users](https://github.com/express-rate-limit/express-slow-down/issues/45#issuecomment-1813098848))
Set `validate: {onLimitReached: false}` in the options to disable the check.
### `WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS`
> Added in `6.10.0`.
The `draft_polli_ratelimit_headers` option was deprecated in express-rate-limit
v6 and removed in version `7.0.0`. Please use the `standardHeaders: 'draft-6'`
option to replicate its behaviour, or use the recommended
`standardHeaders: 'draft-7'` option instead.
Set `validate: {draftPolliHeaders: false}` in the options to disable the check.
[`keyGenerator`]: /reference/configuration#keygenerator
[keyGenerator]: /reference/configuration#keygenerator
# Instance API
Source: https://express-rate-limit.mintlify.app/reference/instance-api
## `resetKey(key)`
Resets the rate limiting for a given key. An example use case is to allow users
to complete a captcha to reset their rate limit, then call this function.
Example:
```ts theme={null}
import { rateLimit } from 'express-rate-limit'
const limiter = rateLimit({
skip: (req) => req.url === '/reset', // Don't limit the reset url!
// ...
})
app.use(limiter)
app.post('/reset', async (req, res) => {
if (/* Validate that they completed the captcha or whatever */) {
limiter.resetKey(req.ip);
res.send('Rate limit is reset!')
} else {
res.status(400).send("Wrong answer, try again.")
}
})
```
If you use a custom [`keyGenerator`](/reference/configuration#keygenerator), be
sure to replace `req.ip` with the correct key.
## `getKey(key)`
Retrieves the hit count and reset time from the store for a given key.
Note: `getKey` depends on store support. It works with the MemoryStore and
cluster-memory-store, but may not work with other stores. Calling it will throw
an error if the store does not have a `get` method.
# Request API
Source: https://express-rate-limit.mintlify.app/reference/request-api
## `req.rateLimit`
A `req.rateLimit` property is added to all requests with the `limit`, `used`,
and `remaining` number of requests and, if the store provides it, a `resetTime`
Date object. These may be used in your application code to take additional
actions or inform the user of their status.
Note that `used` includes the current request, so it should always be > 0.
The property name can be configured with the configuration option
[`requestPropertyName`](/reference/configuration#requestpropertyname).
# Data Stores
Source: https://express-rate-limit.mintlify.app/reference/stores
Express-rate-limit supports external data stores to synchronize hit counts
across multiple processes and servers.
By default, the built-in
[`memory-store`](https://github.com/express-rate-limit/express-rate-limit/blob/main/source/memory-store.ts)
is used. This one does not synchronize it's state across instances. It's simple
to deploy, and often sufficient for basic abuse prevention, but will be
inconsistent across reboots or in deployments with multiple process or servers.
Deployments requiring more consistently enforced rate limits should use an
external store.
Here is a list of known stores:
| Name | Description | Legacy/Modern API |
| --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| [memory-store](https://github.com/express-rate-limit/express-rate-limit/blob/main/source/memory-store.ts) | *(default)* Simple in-memory option. Does not share state when app has multiple processes or servers. | Modern as of v6.0.0 |
| [cluster-memory-store](https://npm.im/@express-rate-limit/cluster-memory-store) | A memory-store wrapper that shares state across all processes on a single server via the [node:cluster](https://nodejs.org/api/cluster.html) module. Does not share state across multiple servers. | Modern |
| [rate-limit-redis](https://npm.im/rate-limit-redis) | A [Redis](http://redis.io/)-backed store, more suitable for large or demanding deployments. | Modern as of v3.0.0 |
| [rate-limit-memcached](https://npmjs.org/package/rate-limit-memcached) | A [Memcached](https://memcached.org/)-backed store. | Modern as of v1.0.0 |
| [rate-limit-mongo](https://www.npm.im/rate-limit-mongo) | A [MongoDB](https://www.mongodb.com/)-backed store. | Legacy |
| [precise-memory-rate-limit](https://www.npm.im/precise-memory-rate-limit) | A memory store similar to the built-in one, except that it stores a distinct timestamp for each key. | Modern as of v2.0.0 |
| [rate-limit-postgresql](https://www.npm.im/@acpr/rate-limit-postgresql) | A [PostgreSQL](https://www.postgresql.org/)-backed store. | Modern |
| [typeorm-rate-limit-store](https://www.npmjs.com/package/typeorm-rate-limit-store) | Supports a variety of databases via [TypeORM](https://typeorm.io/): MySQL, MariaDB, CockroachDB, SQLite, Microsoft SQL Server, Oracle, SAP Hana, and more. | Modern |
| [rate-limit-sqlite](https://www.npmjs.com/package/rate-limit-sqlite) | A [SQLite](https://sqlite.org/index.html)-backed store that uses the [native Node.js SQLite API](https://nodejs.org/api/sqlite.html). | Modern |
Take a look at [this guide](/guides/creating-a-store) if you wish to create your
own store.