page-security-audit

Why does my login rate limit fail with X-Forwarded-For?

Grabzies Ecommerce · 23 September 2026 · 3 replies
My login endpoint is meant to block an IP after 10 failed attempts in 15 minutes. It works when I test normally, but a friend showed me he could keep going forever by adding a different X-Forwarded-For header on every request.

The app sits behind nginx on the same server. The rate limiter reads the client IP from X-Forwarded-For so it gets the real visitor address instead of 127.0.0.1. What am I doing wrong?
3 Replies
John Carlsson · 23 Sept 2026
X-Forwarded-For is a header the client can send, so anyone can put any value in it. Your limiter is counting attempts per value of that header, and an attacker simply changes it on every request, so they never reach 10.

The fix is to read an address the client can't control. If nginx is the only thing in front of your app, set X-Real-IP in the proxy block:

proxy_set_header X-Real-IP $remote_addr;

and have the app read X-Real-IP instead. $remote_addr is the address of whoever actually connected to nginx, which can't be forged by a header. Then test it: send 11 attempts with a different fake X-Forwarded-For each time, and the 11th should still be blocked.
Grabzies Ecommerce · 23 Sept 2026
That worked, thanks. The fake header no longer gets around it and it blocks on attempt 11. Should the same change go anywhere else?
John Carlsson · 23 Sept 2026
Anywhere you use the visitor's IP for something that matters: vote or like counters, contact form logs, free-tier limits, abuse reports. Search your code for x-forwarded-for and check each one. If you're behind Cloudflare or another CDN as well, you'd use their trusted header instead, but only when requests are guaranteed to come through them.

Our Security Audit includes an active rate-limit test on your login page if you want to confirm it from outside.

Join the discussion

Sign in to reply, share your SEO experience and connect with other members.