Short answer: Websites behind a proxy, load balancer or CDN learn the visitor’s IP address from a header such as X-Forwarded-For. A client can send that header too, with any value it likes, so applications that trust it blindly can be tricked into logging fake addresses, skipping IP-based limits or allowing access to restricted pages. Trust the header only when it was set by a proxy you control, and read the address from the correct position in the chain.
Why the header exists
When a visitor connects directly to a web server, the server sees the visitor’s address as the source of the connection. Once a proxy, a load balancer or a content delivery network sits in front, the server sees only the address of that intermediary. The real visitor would be invisible, so proxies add the original address to a header, usually X-Forwarded-For, and some use the standard Forwarded header or a custom one such as CF-Connecting-IP.
Each proxy that handles the request appends the address it received the request from. A request that passed through two proxies might arrive with the value “203.0.113.7, 198.51.100.4”, where the first entry is the original client and the last was added by the nearest proxy. The server’s own view of the connection gives the address of the final hop.
Where the trust problem begins
The header is plain text. Nothing stops a visitor from sending a request with their own X-Forwarded-For line before any proxy touches it. If the proxy simply appends to the existing value, the forged address stays at the front of the list, and an application that takes the first entry will believe it. The attacker has just chosen what the server thinks their address is.
The same holds for any header that carries an address. The mistake is not in the header, it is in treating data from outside as if it came from inside your infrastructure.
What attackers do with it
- Bypass rate limits. If login attempts are limited per IP, an attacker can send each attempt with a different fake address and never reach the limit. See rate limiting and login brute-force protection.
- Evade blocks. A banned address can be hidden behind a harmless one, so that logs and firewalls miss it.
- Reach restricted pages. Some admin tools allow access only from “internal” addresses such as 127.0.0.1 or an office range. A forged header claiming that address can open the door.
- Poison logs and reports. Fake addresses spoil analytics and make it hard to trace an incident. See reading access logs.
- Inject data. The header value ends up in log files, databases and admin screens. If it is not handled as untrusted text, it can carry script or query fragments, which links to cross-site scripting and SQL injection.
The rule: trust only your own proxies
The safe approach has three parts.
- Know your chain. Write down every proxy between the internet and your application: the CDN, the firewall, the load balancer, the local web server. Know their addresses or ranges.
- Accept the header only from them. If the connection does not come from one of your proxies, ignore the forwarded headers and use the connection address. Most servers and frameworks have a setting for trusted proxies; use it instead of custom code.
- Read from the right. Walk the list from the last entry backwards, skipping the addresses of your own proxies, and stop at the first one that is not yours. That is the address your trusted proxy saw, and the entries to the left of it cannot be trusted.
With a CDN that sets its own header with the visitor address, such as a dedicated single-value header, use that one and block direct traffic to your server so nobody can bypass the CDN and send the header themselves. The article on an exposed origin IP behind a CDN explains why this matters.
Common mistakes in configuration
- Taking the first value of X-Forwarded-For without checking who sent it.
- Trusting forwarded headers from any source because “it works in testing”.
- Whitelisting “internal” addresses in headers instead of checking the real connection.
- Leaving the origin server reachable on the public internet, so attackers can skip the CDN.
- Logging the raw header without escaping or length limits.
- Trusting the Host header as well; see host header injection for the same problem with another header.
Frameworks differ in how they handle this, and defaults are not always safe. Some read the leftmost value of the header, some the rightmost and some use a setting that counts how many proxies to skip. Look up what yours does, and write a test that sends a forged header and checks which address the application records. If you change hosting or add a CDN later, repeat the test, because a new layer in front of the server changes the position of the real address in the list. A site that was correct behind one proxy can become wrong behind two.
Also remember that the same address is used in more places than the login page. Spam filters, geographic redirects, license checks, fraud scoring and analytics all depend on it. A wrong address can send a visitor to the wrong language, block a legitimate customer who shares an address with an attacker, or let an abusive client look like many different people. Checking the address once, centrally, and passing the trusted value to every feature is safer than letting each part of the code read the header on its own.
A worked example
A small shop runs behind a CDN. The admin login page allows five wrong attempts per IP address in ten minutes, and the application reads the address from the first entry of X-Forwarded-For. An attacker who wants to guess the owner’s password sends each request with a different random value in that header. To the application every attempt comes from a new visitor, so the limit never triggers, and thousands of guesses are made in an hour. The fix has three steps: configure the application to trust forwarded headers only from the CDN’s ranges, read the address the CDN reports in its own header, and close direct access to the server so requests must pass through the CDN. After the change, the same attack is counted against one address and blocked after five attempts.
It is worth explaining this to the whole team, not only to the developer. Support staff often read addresses from logs when a customer reports a problem, and they should know that an address in a log is evidence only if it came from a trusted source. Agencies that manage several client sites can add the test to their launch checklist: forge the header, look at the log, and note the result. It takes a few minutes and prevents a class of surprises that otherwise appear months later, usually in the middle of an incident when the logs are needed most.
How to check your own setup
- Send a request with a made-up X-Forwarded-For value to your site, for example with a command-line tool, and look at what your logs record. If the fake address appears as the visitor, the header is trusted too widely.
- Check which address your login limit, firewall rules and analytics use.
- Try reaching the server by its direct IP address. If the site answers, the CDN can be bypassed.
- Read the settings for trusted proxies in your web server or framework documentation and set them explicitly.
Do this only on your own sites, and test a staging copy first if the application has sensitive features. A web application firewall can also strip or normalise suspicious headers before they reach the application.
If you cannot change the application, you can still reduce the risk at the edge. Configure the front proxy to overwrite the header instead of appending to it, so that whatever the client sent is discarded and only the address the proxy itself observed is passed on. That single setting removes the forged front of the list, and the application then sees one clean value that it can trust.
See what your site exposes
Header handling is one of several details that decide how safe a site is. Run a free check with Site AI Audit to see your security headers, certificate and exposed software details in a single report.
Related reading
- Rate Limiting Explained: Slow Down Attackers and Bad Bots
- Host Header Injection: How Password Reset Links Get Hijacked
- Origin Server IP Exposed? How Attackers Bypass Your CDN
- DDoS Protection for Small Websites: What You Actually Need
The bottom line
A forwarded address is only a claim. Treat it as true only when it was written by a proxy you control, read it from the right position and keep the origin server closed to direct traffic. Otherwise your limits, blocks and logs rest on a value that any visitor can choose.
DUK
Is X-Forwarded-For a security problem by itself?
No. The header is a useful convention. The problem appears when an application trusts it from sources that are not your own proxies.
Which entry in the list is the real visitor?
The one added by your outermost trusted proxy. Read the list from the right, skip your own proxies and take the first address that is not yours.
Can a visitor fake the connection address too?
Not easily for TCP traffic that needs replies, since the real source address is used by the network. That is why the connection address of the last hop is more reliable than any header.
Does a CDN solve the problem?
It helps if it sets its own header and you accept traffic only from the CDN. If the server is reachable directly, attackers can still send forged headers.
What is the Forwarded header?
A standard version of the same idea with a structured format. It has the same trust issue and needs the same care.



