express-rate-limit 6.11.2 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +56 -15
- package/dist/index.cjs +260 -220
- package/dist/index.d.cts +169 -27
- package/dist/index.d.mts +169 -27
- package/dist/index.d.ts +169 -27
- package/dist/index.mjs +260 -222
- package/license.md +1 -1
- package/package.json +27 -27
- package/readme.md +88 -93
- package/tsconfig.json +4 -1
package/license.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-rate-limit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nathan Friedly",
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
"changelog.md"
|
|
53
53
|
],
|
|
54
54
|
"engines": {
|
|
55
|
-
"node": ">=
|
|
55
|
+
"node": ">= 16"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz",
|
|
59
|
-
"build:cjs": "esbuild --platform=node --bundle --target=
|
|
60
|
-
"build:esm": "esbuild --platform=node --bundle --target=
|
|
59
|
+
"build:cjs": "esbuild --platform=node --bundle --target=es2022 --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;\" source/index.ts",
|
|
60
|
+
"build:esm": "esbuild --platform=node --bundle --target=es2022 --format=esm --outfile=dist/index.mjs source/index.ts",
|
|
61
61
|
"build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts && cp dist/index.d.ts dist/index.d.cts && cp dist/index.d.ts dist/index.d.mts",
|
|
62
62
|
"compile": "run-s clean build:*",
|
|
63
63
|
"lint:code": "xo",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"format:code": "xo --fix",
|
|
67
67
|
"format:rest": "prettier --write .",
|
|
68
68
|
"format": "run-s format:*",
|
|
69
|
-
"test:lib": "
|
|
69
|
+
"test:lib": "jest",
|
|
70
70
|
"test:ext": "cd test/external/ && bash run-all-tests",
|
|
71
71
|
"test": "run-s lint test:lib",
|
|
72
72
|
"pre-commit": "lint-staged",
|
|
@@ -76,28 +76,28 @@
|
|
|
76
76
|
"express": "^4 || ^5"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
79
|
+
"@express-rate-limit/prettier": "1.1.0",
|
|
80
|
+
"@express-rate-limit/tsconfig": "1.0.0",
|
|
81
|
+
"@jest/globals": "29.6.4",
|
|
82
|
+
"@types/express": "4.17.17",
|
|
83
|
+
"@types/jest": "29.5.4",
|
|
84
|
+
"@types/node": "20.5.9",
|
|
85
|
+
"@types/supertest": "2.0.12",
|
|
86
|
+
"cross-env": "7.0.3",
|
|
87
|
+
"del-cli": "5.1.0",
|
|
88
|
+
"dts-bundle-generator": "8.0.1",
|
|
89
|
+
"esbuild": "0.19.2",
|
|
90
|
+
"express": "4.18.2",
|
|
91
|
+
"husky": "8.0.3",
|
|
92
|
+
"jest": "29.6.4",
|
|
93
|
+
"lint-staged": "14.0.1",
|
|
94
|
+
"npm-run-all": "4.1.5",
|
|
95
|
+
"ratelimit-header-parser": "0.1.0",
|
|
96
|
+
"supertest": "6.3.3",
|
|
97
|
+
"ts-jest": "29.1.1",
|
|
98
|
+
"ts-node": "10.9.1",
|
|
99
|
+
"typescript": "5.2.2",
|
|
100
|
+
"xo": "0.56.0"
|
|
101
101
|
},
|
|
102
102
|
"xo": {
|
|
103
103
|
"prettier": true,
|
package/readme.md
CHANGED
|
@@ -25,31 +25,23 @@ Plays nice with
|
|
|
25
25
|
|
|
26
26
|
## Use Cases
|
|
27
27
|
|
|
28
|
-
Depending on your use case, you may
|
|
28
|
+
Depending on your use case, you may want to switch to a different
|
|
29
29
|
[store](#store).
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
### Abuse Prevention
|
|
32
32
|
|
|
33
33
|
The default `MemoryStore` is probably fine.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
### API Rate Limit Enforcement
|
|
36
36
|
|
|
37
|
-
You
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
end of one minute and the next 10 in at the start of the next minute. (After the
|
|
42
|
-
initial burst, they will be limited to the expected 10 requests per minute.) All
|
|
43
|
-
other stores use per-user time windows, so a user will get exactly 10 requests
|
|
44
|
-
regardless.
|
|
45
|
-
|
|
46
|
-
Additionally, if you have multiple servers or processes (for example, with the
|
|
47
|
-
[node:cluster](https://nodejs.org/api/cluster.html) module), you'll likely want
|
|
48
|
-
to use an external data store to syhcnronize hits
|
|
37
|
+
You may want to switch to a different [store](#store), especially if you have
|
|
38
|
+
multiple servers or processes (for example, with the
|
|
39
|
+
[node:cluster](https://nodejs.org/api/cluster.html) module). Using an external
|
|
40
|
+
data store to syhcnronize hits
|
|
49
41
|
([redis](https://npmjs.com/package/rate-limit-redis),
|
|
50
42
|
[memcached](https://npmjs.org/package/rate-limit-memcached), [etc.](#store))
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
guarentees the expected result even if some requests get handled by different
|
|
44
|
+
servers/processes or a server is restarted.
|
|
53
45
|
|
|
54
46
|
### Alternate Rate Limiters
|
|
55
47
|
|
|
@@ -91,7 +83,7 @@ Replace `{version}` with the version of the package that you want to your, e.g.:
|
|
|
91
83
|
This library is provided in ESM as well as CJS forms, and works with both
|
|
92
84
|
Javascript and Typescript projects.
|
|
93
85
|
|
|
94
|
-
**This package requires you to use Node
|
|
86
|
+
**This package requires you to use Node 16 or above.**
|
|
95
87
|
|
|
96
88
|
Import it in a CommonJS project (`type: commonjs` or no `type` field in
|
|
97
89
|
`package.json`) as follows:
|
|
@@ -116,9 +108,9 @@ import { rateLimit } from 'express-rate-limit'
|
|
|
116
108
|
|
|
117
109
|
const limiter = rateLimit({
|
|
118
110
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
119
|
-
|
|
120
|
-
standardHeaders: 'draft-7', // draft-6: RateLimit
|
|
121
|
-
legacyHeaders: false, // X-RateLimit
|
|
111
|
+
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
|
|
112
|
+
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
|
|
113
|
+
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
122
114
|
// store: ... , // Use an external store for more precise rate limiting
|
|
123
115
|
})
|
|
124
116
|
|
|
@@ -135,8 +127,8 @@ import { rateLimit } from 'express-rate-limit'
|
|
|
135
127
|
|
|
136
128
|
const apiLimiter = rateLimit({
|
|
137
129
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
138
|
-
|
|
139
|
-
standardHeaders: 'draft-7', // Set `RateLimit` and `RateLimit-Policy
|
|
130
|
+
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
|
|
131
|
+
standardHeaders: 'draft-7', // Set `RateLimit` and `RateLimit-Policy` headers
|
|
140
132
|
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
141
133
|
// store: ... , // Use an external store for more precise rate limiting
|
|
142
134
|
})
|
|
@@ -152,9 +144,9 @@ import { rateLimit } from 'express-rate-limit'
|
|
|
152
144
|
|
|
153
145
|
const apiLimiter = rateLimit({
|
|
154
146
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
155
|
-
|
|
156
|
-
standardHeaders: 'draft-7', // draft-6: RateLimit
|
|
157
|
-
legacyHeaders: false, // X-RateLimit
|
|
147
|
+
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
|
|
148
|
+
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
|
|
149
|
+
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
158
150
|
// store: ... , // Use an external store for more precise rate limiting
|
|
159
151
|
})
|
|
160
152
|
|
|
@@ -162,14 +154,14 @@ app.use('/api/', apiLimiter)
|
|
|
162
154
|
|
|
163
155
|
const createAccountLimiter = rateLimit({
|
|
164
156
|
windowMs: 60 * 60 * 1000, // 1 hour
|
|
165
|
-
|
|
157
|
+
limit: 5, // Limit each IP to 5 create account requests per `window` (here, per hour)
|
|
166
158
|
message:
|
|
167
159
|
'Too many accounts created from this IP, please try again after an hour',
|
|
168
|
-
standardHeaders: 'draft-7', // draft-6: RateLimit
|
|
169
|
-
legacyHeaders: false, // X-RateLimit
|
|
160
|
+
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
|
|
161
|
+
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
170
162
|
})
|
|
171
163
|
|
|
172
|
-
app.post('/create-account', createAccountLimiter, (
|
|
164
|
+
app.post('/create-account', createAccountLimiter, (req, res) => {
|
|
173
165
|
//...
|
|
174
166
|
})
|
|
175
167
|
```
|
|
@@ -184,9 +176,9 @@ import RedisClient from 'ioredis'
|
|
|
184
176
|
const redisClient = new RedisClient()
|
|
185
177
|
const rateLimiter = rateLimit({
|
|
186
178
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
187
|
-
|
|
188
|
-
standardHeaders: 'draft-7', // draft-6: RateLimit
|
|
189
|
-
legacyHeaders: false, // X-RateLimit
|
|
179
|
+
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
|
|
180
|
+
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
|
|
181
|
+
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
190
182
|
store: new RedisStore({
|
|
191
183
|
/* ... */
|
|
192
184
|
}), // Use the external store
|
|
@@ -202,34 +194,9 @@ app.use(rateLimiter)
|
|
|
202
194
|
|
|
203
195
|
### Troubleshooting Proxy Issues
|
|
204
196
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
request might be the IP of the load balancer/reverse proxy (making the rate
|
|
209
|
-
limiter effectively a global one and blocking all requests once the limit is
|
|
210
|
-
reached) or `undefined`. To solve this issue, add the following line to your
|
|
211
|
-
code (right after you create the express application):
|
|
212
|
-
|
|
213
|
-
```ts
|
|
214
|
-
app.set('trust proxy', numberOfProxies)
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
Where `numberOfProxies` is the number of proxies between the user and the
|
|
218
|
-
server. To find the correct number, create a test endpoint that returns the
|
|
219
|
-
client IP:
|
|
220
|
-
|
|
221
|
-
```ts
|
|
222
|
-
app.set('trust proxy', 1)
|
|
223
|
-
app.get('/ip', (request, response) => response.send(request.ip))
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
Go to `/ip` and see the IP address returned in the response. If it matches your
|
|
227
|
-
public IP address, then the number of proxies is correct and the rate limiter
|
|
228
|
-
should now work correctly. If not, then keep increasing the number until it
|
|
229
|
-
does.
|
|
230
|
-
|
|
231
|
-
For more information about the `trust proxy` setting, take a look at the
|
|
232
|
-
[official Express documentation](https://expressjs.com/en/guide/behind-proxies.html).
|
|
197
|
+
Please take a look at
|
|
198
|
+
[the wiki page](https://github.com/express-rate-limit/express-rate-limit/wiki/Troubleshooting-Proxy-Issues)
|
|
199
|
+
on this issue.
|
|
233
200
|
|
|
234
201
|
## Configuration
|
|
235
202
|
|
|
@@ -247,7 +214,7 @@ twice, once here and once on the store. In some cases the units also differ
|
|
|
247
214
|
|
|
248
215
|
Defaults to `60000` ms (= 1 minute).
|
|
249
216
|
|
|
250
|
-
### `
|
|
217
|
+
### `limit`
|
|
251
218
|
|
|
252
219
|
> `number | function`
|
|
253
220
|
|
|
@@ -255,9 +222,16 @@ The maximum number of connections to allow during the `window` before rate
|
|
|
255
222
|
limiting the client.
|
|
256
223
|
|
|
257
224
|
Can be the limit itself as a number or a (sync/async) function that accepts the
|
|
258
|
-
Express `
|
|
225
|
+
Express `req` and `res` objects and then returns a number.
|
|
259
226
|
|
|
260
|
-
|
|
227
|
+
~Set it to `0` to disable the rate limiter.~ As of version 7.0.0, setting `max`
|
|
228
|
+
to zero will no longer disable the rate limiter - instead, it will 'block' all
|
|
229
|
+
requests to that endpoint.
|
|
230
|
+
|
|
231
|
+
Defaults to `5`.
|
|
232
|
+
|
|
233
|
+
> Renamed in v7.x from `max` to `limit`. However, `max` will still be supported
|
|
234
|
+
> for backwards-compatibility.
|
|
261
235
|
|
|
262
236
|
An example of using a function:
|
|
263
237
|
|
|
@@ -268,8 +242,8 @@ const isPremium = async (user) => {
|
|
|
268
242
|
|
|
269
243
|
const limiter = rateLimit({
|
|
270
244
|
// ...
|
|
271
|
-
|
|
272
|
-
if (await isPremium(
|
|
245
|
+
limit: async (req, res) => {
|
|
246
|
+
if (await isPremium(req.user)) return 10
|
|
273
247
|
else return 5
|
|
274
248
|
},
|
|
275
249
|
})
|
|
@@ -282,10 +256,10 @@ const limiter = rateLimit({
|
|
|
282
256
|
The response body to send back when a client is rate limited.
|
|
283
257
|
|
|
284
258
|
May be a `string`, JSON object, or any other value that Express's
|
|
285
|
-
[`
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
259
|
+
[`res.send`](https://expressjs.com/en/4x/api.html#res.send) method supports. It
|
|
260
|
+
can also be a (sync/async) function that accepts the Express request and
|
|
261
|
+
response objects and then returns a `string`, JSON object or any other value the
|
|
262
|
+
Express `res.send` function accepts.
|
|
289
263
|
|
|
290
264
|
Defaults to `'Too many requests, please try again later.'`
|
|
291
265
|
|
|
@@ -298,8 +272,8 @@ const isPremium = async (user) => {
|
|
|
298
272
|
|
|
299
273
|
const limiter = rateLimit({
|
|
300
274
|
// ...
|
|
301
|
-
message: async (
|
|
302
|
-
if (await isPremium(
|
|
275
|
+
message: async (req, res) => {
|
|
276
|
+
if (await isPremium(req.user))
|
|
303
277
|
return 'You can only make 10 requests every hour.'
|
|
304
278
|
else return 'You can only make 5 requests every hour.'
|
|
305
279
|
},
|
|
@@ -422,7 +396,7 @@ By default, the client's IP address is used:
|
|
|
422
396
|
```ts
|
|
423
397
|
const limiter = rateLimit({
|
|
424
398
|
// ...
|
|
425
|
-
keyGenerator: (
|
|
399
|
+
keyGenerator: (req, res) => req.ip,
|
|
426
400
|
})
|
|
427
401
|
```
|
|
428
402
|
|
|
@@ -443,8 +417,8 @@ similar to this:
|
|
|
443
417
|
```ts
|
|
444
418
|
const limiter = rateLimit({
|
|
445
419
|
// ...
|
|
446
|
-
handler: (
|
|
447
|
-
|
|
420
|
+
handler: (req, res, next, options) =>
|
|
421
|
+
res.status(options.statusCode).send(options.message),
|
|
448
422
|
})
|
|
449
423
|
```
|
|
450
424
|
|
|
@@ -452,9 +426,8 @@ const limiter = rateLimit({
|
|
|
452
426
|
|
|
453
427
|
> `function`
|
|
454
428
|
|
|
455
|
-
A (sync/async) function that accepts the Express `
|
|
456
|
-
|
|
457
|
-
rate limit.
|
|
429
|
+
A (sync/async) function that accepts the Express `req` and `res` objects that is
|
|
430
|
+
called the on the request where a client has just exceeded their rate limit.
|
|
458
431
|
|
|
459
432
|
This method was
|
|
460
433
|
[deprecated in v6](https://github.com/express-rate-limit/express-rate-limit/releases/v6.0.0) -
|
|
@@ -475,7 +448,7 @@ const allowlist = ['192.168.0.56', '192.168.0.21']
|
|
|
475
448
|
|
|
476
449
|
const limiter = rateLimit({
|
|
477
450
|
// ...
|
|
478
|
-
skip: (
|
|
451
|
+
skip: (req, res) => allowlist.includes(req.ip),
|
|
479
452
|
})
|
|
480
453
|
```
|
|
481
454
|
|
|
@@ -484,7 +457,7 @@ By default, it skips no requests:
|
|
|
484
457
|
```ts
|
|
485
458
|
const limiter = rateLimit({
|
|
486
459
|
// ...
|
|
487
|
-
skip: (
|
|
460
|
+
skip: (req, res) => false,
|
|
488
461
|
})
|
|
489
462
|
```
|
|
490
463
|
|
|
@@ -494,8 +467,8 @@ const limiter = rateLimit({
|
|
|
494
467
|
|
|
495
468
|
Method to determine whether or not the request counts as 'succesful'. Used when
|
|
496
469
|
either `skipSuccessfulRequests` or `skipFailedRequests` is set to true. Should
|
|
497
|
-
be a (sync/async) function that accepts the Express `
|
|
498
|
-
|
|
470
|
+
be a (sync/async) function that accepts the Express `req` and `res` objects and
|
|
471
|
+
then returns `true` or `false`.
|
|
499
472
|
|
|
500
473
|
By default, requests with a response status code less than 400 are considered
|
|
501
474
|
successful:
|
|
@@ -503,24 +476,43 @@ successful:
|
|
|
503
476
|
```ts
|
|
504
477
|
const limiter = rateLimit({
|
|
505
478
|
// ...
|
|
506
|
-
requestWasSuccessful: (
|
|
479
|
+
requestWasSuccessful: (req, res) => res.statusCode < 400,
|
|
507
480
|
})
|
|
508
481
|
```
|
|
509
482
|
|
|
510
483
|
### `validate`
|
|
511
484
|
|
|
512
|
-
> `boolean`
|
|
485
|
+
> `boolean | Object`
|
|
513
486
|
|
|
514
|
-
When enabled, a set of validation checks are run on the first
|
|
515
|
-
common misconfigurations with proxies, etc. Prints an error to
|
|
516
|
-
any issue is detected.
|
|
487
|
+
When enabled, a set of validation checks are run at creation and on the first
|
|
488
|
+
request to detect common misconfigurations with proxies, etc. Prints an error to
|
|
489
|
+
the console if any issue is detected.
|
|
517
490
|
|
|
518
491
|
Automatically disables after the first request is processed.
|
|
519
492
|
|
|
493
|
+
If set to `true` or `false`, all validations are enabled or disabled.
|
|
494
|
+
|
|
495
|
+
If set to an object, individual validations can be enabled or disabled by name,
|
|
496
|
+
and the key `default` controls all unspecified validations. For example:
|
|
497
|
+
|
|
498
|
+
```js
|
|
499
|
+
const limiter = rateLimit({
|
|
500
|
+
validate: {
|
|
501
|
+
xForwardedForHeader: false,
|
|
502
|
+
default: true,
|
|
503
|
+
},
|
|
504
|
+
// ...
|
|
505
|
+
})
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Supported options are `ip`, `trustProxy`, `xForwardedForHeader`, `positiveHits`,
|
|
509
|
+
`singleCount`, `limit`, `draftPolliHeaders`, `onLimitReached`,
|
|
510
|
+
`headersResetTime`, `validationsConfig`, and `default`.
|
|
511
|
+
|
|
520
512
|
See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes
|
|
521
513
|
for more info.
|
|
522
514
|
|
|
523
|
-
Defaults to true
|
|
515
|
+
Defaults to `true`.
|
|
524
516
|
|
|
525
517
|
### `store`
|
|
526
518
|
|
|
@@ -546,10 +538,12 @@ if you wish to create your own store.
|
|
|
546
538
|
|
|
547
539
|
## Request API
|
|
548
540
|
|
|
549
|
-
A `
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
541
|
+
A `req.rateLimit` property is added to all requests with the `limit`, `used`,
|
|
542
|
+
and `remaining` number of requests and, if the store provides it, a `resetTime`
|
|
543
|
+
Date object. These may be used in your application code to take additional
|
|
544
|
+
actions or inform the user of their status.
|
|
545
|
+
|
|
546
|
+
Note that `used` includes the current request, so it should always be > 0.
|
|
553
547
|
|
|
554
548
|
The property name can be configured with the configuration option
|
|
555
549
|
`requestPropertyName`.
|
|
@@ -576,4 +570,5 @@ fix/implement it!
|
|
|
576
570
|
|
|
577
571
|
## License
|
|
578
572
|
|
|
579
|
-
MIT © [Nathan Friedly](http://nfriedly.com/)
|
|
573
|
+
MIT © [Nathan Friedly](http://nfriedly.com/),
|
|
574
|
+
[Vedant K](https://github.com/gamemaker1)
|