X-Forwarded-For and IIS

What the header actually contains, why IIS has never read it natively, and what your options are on Windows Server. Start here if your IIS logs are full of your load balancer's address.

Short answer. X-Forwarded-For is the de facto standard request header that proxies, load balancers and CDNs use to pass on the address of the original client. IIS does not read it. Not in IIS 7, not in IIS 8.5, not in IIS 10. The c-ip field in an IIS log is filled from the TCP connection, and behind a proxy the only connection IIS sees is the one from the proxy, so c-ip records the proxy. The header arrives on every request and is ignored. Closing that gap means getting the header value into c-ip, and trusting only the hop that set it.

Does IIS support X-Forwarded-For?

Not natively, and this is the single most common misunderstanding on the topic. IIS parses the header the same way it parses any other request header, so an application running on IIS can read it. What IIS will not do on its own is act on it: nothing in the platform substitutes the forwarded address into the request context, into c-ip, or into anything else that logging and security tooling looks at.

Every other major web server ships something for this. Apache has mod_remoteip, nginx has real_ip. IIS has no equivalent module in the box, which is why the question keeps coming up and why the answers scattered across forums are all third-party components, rewrite tricks or application code.

What is actually in the header

It is a comma-separated list, not a single address, and it grows left to right as a request passes through each hop:

X-Forwarded-For: 203.0.113.7, 198.51.100.24, 192.0.2.60

Each proxy appends the address it received the connection from. So the leftmost entry is the furthest from you, and it is the one entirely under the caller's control: anyone can send a request that already contains an X-Forwarded-For header with any addresses they like, and your proxies will simply append to it. The rightmost entry is the one your own nearest hop added, and it is the only one you observed rather than inherited.

This is why "just log the leftmost value" is wrong, and why any solution that does it can be trivially spoofed. The reliable client address is found by reading the chain from the right and stopping at the first address that is not a proxy you trust. The full breakdown, including which header each vendor sends and the ones that are not X-Forwarded-For at all, is in which client IP header does your proxy send?

Three different questions get asked as one

"X-Forwarded-For and IIS" usually means one of three things, and they have different answers:

What you actually want Where it lands Answer
My IIS logs show the proxy and I need the real visitor. The c-ip field of the W3C log, read by your SIEM, geo-IP and reporting. Not solvable with native IIS configuration. See what IIS gives you natively below.
My application code sees the wrong address. HttpContext.Connection.RemoteIpAddress, Request.UserHostAddress, REMOTE_ADDR. ASP.NET Core has forwarded-headers middleware for exactly this. See UseForwardedHeaders behind a proxy. Note it fixes the application only, never the IIS log.
My proxy is not sending the header, or sends a different one. Upstream, on the load balancer or CDN. Depends on the vendor, and several do not use X-Forwarded-For as the primary header. See the header reference or the per-vendor guides below.

What IIS gives you natively, and where it stops

Since IIS 8.5 there has been a genuinely useful native feature: custom log fields, which can pull a named request header into the W3C log. You can add X-Forwarded-For that way and it costs nothing.

What it does not do is change c-ip. A custom field is an extra column holding the raw header value, complete with the whole untrusted chain and whatever anyone chose to forge into it. c-ip, the column every downstream tool already reads, keeps showing the proxy. So the native option is worth having for troubleshooting and worthless for anything that consumes c-ip, which is usually the reason people came looking. The mechanics of setting it up, on IIS 8.5 and later and on older versions, plus a PowerShell method, are in how to log X-Forwarded-For in IIS.

The other routes people find in forums have their own problems. URL Rewrite rules that overwrite REMOTE_ADDR change what the application sees but not the log, and are easy to write in a way that silently fails for IPv6 clients. Old vendor ISAPI filters exist for some load balancers but are unmaintained and largely will not load on modern IIS: the F5 filter is the best known example.

The fix: X-Forwarded-For for IIS

Winfrasoft X-Forwarded-For for IIS is an ISAPI web filter that reads the header and writes the real client address into the standard IIS c-ip field, so existing reporting, geolocation and SIEM pipelines see the right address with no application changes and no new log file. A Proxy Trust List holds the addresses of your own proxy hops, so the filter walks the chain from the right, skips the hops you trust, and logs the first address you did not. That is what makes the value trustworthy rather than merely present.

Guides by what sits in front of IIS

The mechanism is identical everywhere. What changes is the header name and the addresses you need to trust.

Frequently asked questions

Does IIS support X-Forwarded-For natively?

No. No version of IIS, including IIS 10 on Windows Server 2025, reads X-Forwarded-For and acts on it. The header is available to application code like any other request header, but IIS never substitutes it into the c-ip log field or the request context. Apache has mod_remoteip and nginx has real_ip; IIS ships no equivalent.

Why does c-ip show my load balancer instead of the visitor?

Because c-ip is filled from the TCP connection, and behind a reverse proxy the only connection IIS sees is the one the proxy opened. The visitor's address is present in the X-Forwarded-For header on the same request, but nothing in IIS connects the two.

Can I add X-Forwarded-For to my IIS logs without extra software?

Yes, as a custom log field on IIS 8.5 and later. It adds an extra column containing the raw header. It does not change c-ip, and the raw header includes the full untrusted chain, so anything reading c-ip still sees the proxy and anything reading the new column has to do its own trust evaluation.

Which address in X-Forwarded-For is the real client?

Not necessarily the leftmost one, despite that being the common advice. The header is a chain that each hop appends to, so the leftmost entry can be anything the caller chose to send. Read from the right, skip the addresses belonging to proxies you control, and the first address that is not one of yours is the real client.

Can X-Forwarded-For be spoofed?

Trivially. It is just a request header, so any client can send one containing any address. It becomes trustworthy only when you know which hops are yours and ignore everything to the left of them, and when clients cannot reach your IIS servers directly and bypass the proxy altogether. Both halves matter.

Does UseForwardedHeaders in ASP.NET Core fix the IIS log?

No. The forwarded-headers middleware rewrites what your application sees, so HttpContext.Connection.RemoteIpAddress becomes correct, but it runs inside your application and IIS has already written the log entry from the connection. The IIS c-ip field is unaffected.

Get X-Forwarded-For for IIS

Log the real client IP in the standard IIS c-ip field, behind any proxy, load balancer or CDN. Available now for IIS 10 on Windows Server 2016–2025, with a free 14-day trial.

View the product   or request a download →