5

I'm using express-rate-limit npm package, I deployed my backend on AWS (t2 micro ec2 instance), while limiter is on, requests are blocked from ALL users who try to interact with my API, it works for a couple of minutes and stops for about 10 minutes. when I comment out the limiter part everything is working fine,I think too many requests should be blocked for only one user who tries to hammer the server with requests but what happens is ALL users get blocked, all users are treated like only 1 user, that's my conclusion.

If that's the case what should I do? I need my rate limiter on, and if there is any other explanation what would it be?

Omar Zahir
  • 195
  • 1
  • 3
  • 14
  • Hello Omar, I think that `express-rate-limit` blocks by IP, so, if you make multiple requests from the same IP they will be blocked when the `limit` that you defined is reached. To prove my point I would recommend you to [check this out](https://github.com/nfriedly/express-rate-limit#request-api). Hope that helps, sigfried. – Rubén S. García Oct 03 '20 at 20:34
  • yes I know that from the docs, my point is I think there may be some server config that simulates that all requests are from the same user? – Omar Zahir Oct 03 '20 at 21:13
  • 1
    Maybe your application is been served with reversal proxy... all the requests are made to your API with the same IP... try to add app.set('trust proxy', 1); to your server config. – Reculos Gerbi Neto Apr 08 '21 at 15:37
  • @OmarZahir, can you accept my answer if it answers your question? – kmanzana May 13 '22 at 23:01

3 Answers3

8

By default, express-rate-limit has a keyGenerator of req.ip. When I log this on my server it is '::ffff:127.0.0.1' which is obviously going to be the same for every request, thus limiting for all IP addresses once it's limited for one.

My solution was to use request-ip to get the correct IP address like so:

const rateLimit = require('express-rate-limit');
const requestIp = require('request-ip');

const app = express();

app.use(requestIp.mw());

app.use(rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 30, // limit each IP to 30 requests per windowMs
  keyGenerator: (req, res) => {
    return req.clientIp // IP address from requestIp.mw(), as opposed to req.ip
  }
}));
kmanzana
  • 1,138
  • 1
  • 13
  • 23
  • 2
    This solution worked. Hope this is not going to create any issues. – r.k Feb 23 '22 at 06:12
  • 1
    This appears to work, but leaves you open to attack by a malicious user who sets headers with arbitrary incorrect IPs. Depending on the configuration of the reverse proxy, these headers may make it through to the app allowing this malicious user to bypass the rate limits. Using express's built-in `app.set('trust proxy', 1)` setting is a bit safer because it trusts only the nearest proxy. (I'm the author of express-rate-limit.) – Nathan Friedly Jul 23 '23 at 15:15
2
keyGenerator: function (req: any) {
    return req.headers["x-forwarded-for"] || req.connection.remoteAddress; 
}

It blocks based on iP

FBC
  • 1,047
  • 8
  • 18
  • This leaves you vulnerable to a malicious user setting X-Forwarded-For with a different random IP on each request. Express's `app.set('trust proxy', 1)` is safer because it only trusts the closest proxy. (Change to `2`, `3`, etc. if there are multiple proxies between the server and the internet.) – Nathan Friedly Aug 11 '23 at 15:00
1

The express-rate-limit package blocks requests based on IP Address and that's because it provides a very basic configuration for rate-limiting that would be suitable for most applications. If you block based on user, someone can easily configure a bot to hit your APIs until the limit is reached on one user account and make a new account automatically to start hitting your server again. Blocking based on IP avoids such risks as one IP means one Device no matter how many users request from that IP. In most cases, one device is most likely to be used by one person so this solution works pretty well.

Ahmad
  • 11
  • 1
  • 1
    What he means that once an IP blocked because of too many requests, the service responds to all requests with 429. This is outrageously buggy code. – Burak Karakuş Feb 18 '21 at 19:48