# 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.