The Reverse Proxy Everyone Knows
If you've spent any time building for the web, you've encountered the reverse proxy. In its simplest form, it’s a server that sits in front of your application servers, acting as a gatekeeper for all incoming traffic. Instead of users connecting directly
to your application, they connect to the reverse proxy, which then forwards their requests. The most common reasons for this are well-known: distributing traffic across multiple servers (load balancing), speeding up response times by storing common files (caching), and handling encrypted connections (SSL termination). For many self-taught developers, this is where the lesson ends. You set up NGINX or a cloud load balancer, point it at your app, and it just works. Your application is faster, more reliable, and you've hidden its direct IP address from the public internet. It feels like a solid win.
The Detail Hiding in Plain Sight
The problem begins when your application needs to know who the user is—specifically, their IP address. When a request comes through a reverse proxy, the direct connection to your application is from the proxy itself, not the user. To your app server, every single request looks like it’s coming from the same place: the proxy’s IP. To solve this, a de-facto standard emerged: a special HTTP header called `X-Forwarded-For`. The reverse proxy adds this header to the request it sends to your application, and in it, places the original IP address of the user. Your application can then read this header to get the real client IP for things like logging, analytics, or applying country-specific rules. Simple, right? Just read the header.
Why This Detail Is a Security Trap
Here is the hidden detail most engineers miss: the `X-Forwarded-For` header is fundamentally untrustworthy unless you configure your infrastructure to enforce trust. Because HTTP headers can be set by anyone, a malicious user can send a request to your proxy with a fake `X-Forwarded-For` header already present. For example, they could put the IP address of an administrator or an internal service in the header. If your proxy is naively configured to just append the client IP to an existing header, or if your application simply trusts the first IP it sees, you have a major vulnerability. An attacker can effectively spoof their IP address. This allows them to bypass IP-based security rules, like rate limits designed to stop brute-force attacks or access controls that restrict admin panels to specific IP addresses. Your application might think a request is from a trusted source when it’s actually from an attacker.
The Right Way to Handle It
The solution isn't to stop using `X-Forwarded-For`, but to treat it with suspicion. The core principle is that your application should only trust the IP address information added by your own infrastructure. A properly configured reverse proxy at the edge of your network should be set up to strip any incoming `X-Forwarded-For` headers from the client's request. It then creates a new header, placing the connecting IP address—which it knows to be legitimate—as the sole value. This ensures that when the request reaches your application server, the `X-Forwarded-For` header contains a value that was set by a trusted component you control. It's a subtle but crucial distinction: don't append to a potentially fake list; create a fresh, trustworthy one. This closes the IP spoofing loophole and makes your security checks meaningful again.













