|
1 | 1 | import rateLimit from 'express-rate-limit' |
2 | | -import RedisStore from 'rate-limit-redis' |
3 | | -import createRedisClient from '../lib/redis/create-client.js' |
| 2 | +import statsd from '../lib/statsd.js' |
4 | 3 |
|
5 | | -const isProduction = process.env.NODE_ENV === 'production' |
6 | | -const { REDIS_URL } = process.env |
7 | | -const rateLimitDatabaseNumber = 0 |
8 | 4 | const EXPIRES_IN_AS_SECONDS = 60 |
9 | 5 |
|
10 | | -// The reason the options object is created outside like this is for the |
11 | | -// necessity of avoiding setting a key called `store` even if it's set |
12 | | -// to `undefined`. |
13 | | -// More context here: https://github.com/nfriedly/express-rate-limit/issues/289 |
14 | | -const options = { |
15 | | - // 1 minute (or practically unlimited outside of production) |
16 | | - windowMs: isProduction ? EXPIRES_IN_AS_SECONDS * 1000 : 1, // Non-Redis configuration in `ms`. Used as a fallback when Redis is not working or active. |
| 6 | +export default rateLimit({ |
| 7 | + // 1 minute |
| 8 | + windowMs: EXPIRES_IN_AS_SECONDS * 1000, |
17 | 9 | // limit each IP to X requests per windowMs |
18 | | - max: 250, |
19 | | - // Don't rate limit requests for 200s and redirects |
20 | | - // Or anything with a status code less than 400 |
21 | | - skipSuccessfulRequests: true, |
22 | | -} |
| 10 | + // We currently have about 25 instances in production. That's routed |
| 11 | + // in Azure to spread the requests to each healthy instance. |
| 12 | + // So, the true rate limit, per `windowMs`, is this number multiplied |
| 13 | + // by the current number of instances. |
| 14 | + // We have see DDoS attempts against prod that hits the `/` endpoint |
| 15 | + // (and not following the redirect to `/en`) at roughly 200k per minute. |
| 16 | + max: 100, |
23 | 17 |
|
24 | | -// When available, use Redis; if not, defaults to an in-memory store |
25 | | -if (REDIS_URL) { |
26 | | - options.store = new RedisStore({ |
27 | | - client: createRedisClient({ |
28 | | - url: REDIS_URL, |
29 | | - db: rateLimitDatabaseNumber, |
30 | | - name: 'rate-limit', |
31 | | - }), |
32 | | - // 1 minute (or practically unlimited outside of production) |
33 | | - expiry: isProduction ? EXPIRES_IN_AS_SECONDS : 1, // Redis configuration in `s` |
34 | | - // If Redis is not connected, let the request succeed as failover |
35 | | - passIfNotConnected: true, |
36 | | - }) |
37 | | -} |
38 | | - |
39 | | -export default rateLimit(options) |
| 18 | + handler: (request, response, next, options) => { |
| 19 | + const tags = [`url:${request.url}`, `ip:${request.ip}`] |
| 20 | + statsd.increment('rate_limit', 1, tags) |
| 21 | + // NOTE! At the time of writing, the actual rate limiting is disabled! |
| 22 | + // At least we can start recording how often this happens in Datadog. |
| 23 | + // The following line is commented out and replaced with `next()` |
| 24 | + // response.status(options.statusCode).send(options.message) |
| 25 | + next() |
| 26 | + }, |
| 27 | +}) |
0 commit comments