Fix "X-Forwarded-For" header validation error on implementing API rate limiting

You've just configured rate limiting on your API endpoints but performing a little test shows that you're getting rate limited when you're sure you shouldn't be. I was right in your shoes and the solution was a pretty interesting one. The logs are shown below:

ValidationError: The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users. See https://express-rate-limit.github.io/ERR_ERL_UNEXPECTED_X_FORWARDED_FOR/ for more information.
    at Object.xForwardedForHeader (/opt/render/project/src/node_modules/express-rate-limit/dist/index.cjs:166:13)
    at Object.wrappedValidations.<computed> [as xForwardedForHeader] (/opt/render/project/src/node_modules/express-rate-limit/dist/index.cjs:324:22)
    at Object.keyGenerator (/opt/render/project/src/node_modules/express-rate-limit/dist/index.cjs:579:20)
    at /opt/render/project/src/node_modules/express-rate-limit/dist/index.cjs:629:32
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async /opt/render/project/src/node_modules/express-rate-limit/dist/index.cjs:611:5 {
  code: 'ERR_ERL_UNEXPECTED_X_FORWARDED_FOR',
  help: 'https://express-rate-limit.github.io/ERR_ERL_UNEXPECTED_X_FORWARDED_FOR/'
}

If you are behind a proxy/load balancer, which is usually the case with most hosting services like Render, Heroku, Cloudflare et al., 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.

The error above is logged when the X-Forwarded-For header (which indicates the use of a proxy) is set, but the trust proxy setting still has its default value of false

To solve this issue, add the following line to your code (right after you create the express application)

app.set('trust proxy', 1)
app.get('/ip', (request, response) => response.send(request.ip))
app.get('/x-forwarded-for', (request, response) => response.send(request.headers['x-forwarded-for']))

The value of trust proxy is the number of proxies between the user and the server. To find the correct number, you'd need to redeploy the application, and send a request while incrementing from 1..2..3...n until the request's IP matches your IP address (you can check https://ip.nfriedly.com/)

If you test this locally, you will only get back an IP of "::1" and an empty X-Forwarded-For response because you're running locally, of course.

Quick hint: The number of IP addresses in GET /x-forwarded-for response would give a good estimation of the number of proxies. Just so you don't search too long :-)