2026-10-01 · 6 min read · Rahul Chitturi
- http
- proxy
- security
The Forwarded header, from RFC 7239, records the client address, the receiving interface, the original host, and the protocol of a proxied request. A client can send that header too. An application that trusts the first for value will record whatever address the caller invented.
X-Forwarded-For is a different header with a different grammar. A proxy can set one and leave the other untouched. Debugging only the header your framework happens to read hides the spoofed value in the other one.
Identify the element your edge proxy added
A safe edge proxy deletes inbound Forwarded and X-Forwarded-For, then writes one element for the connection it accepted. After that, the application can trust the value. If the proxy appends instead of replacing, earlier elements are still attacker-controlled and the proxy's element is the one it added, not automatically the leftmost address.
IPv6 values are quoted and wrapped in brackets, as in for="[2001:db8::1]". unknown and an identifier that starts with an underscore are obfuscated values, not hostnames you should resolve.
- Capture the header on the wire before the app parses it.
- Note whether the edge proxy replaces or appends.
- Parse for, by, host, and proto as separate parameters.
- Ignore a client-supplied chain that never passed the trusted proxy.
Check proto and host, not only the client IP
proto and host decide absolute redirects, secure-cookie flags, and canonical URLs. A forged proto can make an app emit an https redirect from a request that arrived as http, or the reverse, if the app trusts the header ahead of the proxy.
Comma-separated elements are separate hops. A comma inside a quoted IPv6 address is not a new hop. Split elements first, then split parameters on semicolons.
Fix the trust boundary, then retest the chain
Configure the edge proxy to strip inbound forwarding headers from the public internet. Teach the app the proxy's address or hop count, and document which header is authoritative. Keep X-Forwarded-For and Forwarded on that same policy so a framework default cannot resurrect the discarded header.
Use Flashman's URL tool on the host and proto you will generate, the JSON formatter for the parsed elements, diff for the header before and after the proxy, and the regex tester for the split between hops. Include a direct request, a single proxy, a quoted IPv6, and a client that already sent a fake for value.