Unlike a cloud load balancer, Nginx and HAProxy forward nothing about the original client unless you configure them to. Here's the one directive each needs, and how to get the real address into your IIS logs once it's arriving.
Short answer. Neither proxy sends X-Forwarded-For by default. In Nginx add proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; to the location block; in HAProxy add option forwardfor to the backend. That gets the visitor's address as far as IIS, which still won't log it, because IIS doesn't read the header. Install an ISAPI filter that writes it into the standard c-ip field, trusting only your proxy's addresses.
Nginx and HAProxy both terminate the client connection and open a new one to your IIS backend. As far as IIS is concerned every request originates from the proxy, so the c-ip field in your logs shows the same one or two addresses for all traffic, and geolocation, reporting, rate-limiting and security tooling lose sight of the real visitor.
What makes these two different from Cloudflare or an AWS load balancer is that they add nothing on your behalf. A managed service inserts X-Forwarded-For automatically; Nginx and HAProxy are yours to configure, and out of the box they forward the request without any record of who sent it. If the header never gets added upstream, no amount of work on the IIS side can recover the address.
In the location block that proxies to IIS:
location / {
proxy_pass http://iis_backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
The variable that matters is $proxy_add_x_forwarded_for. It appends the connecting address to any X-Forwarded-For header already present, rather than replacing it, which is what preserves the chain when Nginx sits behind another proxy or a CDN. Setting the header to $remote_addr instead would discard everything upstream of Nginx.
Reload with nginx -s reload after testing the config with nginx -t.
In the backend serving your IIS servers:
backend iis_servers
option forwardfor
server web1 10.0.0.11:80 check
server web2 10.0.0.12:80 check
option forwardfor adds the client's address to X-Forwarded-For. Placing it in the defaults section applies it to every backend at once. If HAProxy itself sits behind another proxy, add except with that proxy's address so HAProxy doesn't record the intermediate hop as the client.
The header now reaches your web servers, but IIS doesn't read X-Forwarded-For natively. Microsoft never built it in, so the header arrives and is discarded, and c-ip still shows the proxy. That's the gap Winfrasoft X-Forwarded-For for IIS fills.
It's an ISAPI web filter that reads the header and writes the real client address into the standard IIS c-ip log field. Nothing changes in your applications or log format, so existing SIEM and log-analysis tooling keeps working unchanged. A Proxy Trust List of your Nginx or HAProxy addresses ensures only that hop is trusted, so the genuine visitor is logged and the header can't be spoofed.
$http_x_forwarded_for at the proxy, or capture a request at the backend.IISRESET, then browse the site and confirm the IIS c-ip field shows real visitor addresses.Lock down the backend. Because you control this proxy rather than renting it, the origin is often reachable on the same network, which means anyone who can reach IIS directly can forge an X-Forwarded-For header. Restrict the backend firewall or IIS IP restrictions to accept web traffic only from your proxy, so the header can only arrive through it.
No. Nginx forwards the request without adding any client-IP header unless you configure proxy_set_header yourself. This is the most common reason a proxied IIS site logs only the Nginx address — the header was never sent in the first place.
$remote_addr is just the address Nginx received the connection from. $proxy_add_x_forwarded_for is that address appended to any existing X-Forwarded-For header, preserving the chain when there's a CDN or another proxy in front. Use the latter unless you have a specific reason not to.
X-Real-IP is a common Nginx convention holding a single address. It's simpler to parse but carries no proxy chain, so it can't be validated against a trust list, and it isn't a cross-vendor standard. X-Forwarded-For is the better choice for logging. See which header each proxy sends.
In a backend section to apply it to that backend, or in defaults to apply it to all of them. It takes effect for HTTP-mode proxying; in TCP mode HAProxy isn't parsing HTTP at all, so use the PROXY protocol instead.
Because IIS doesn't read it. The c-ip field records the address of whatever opened the TCP connection, and nothing in IIS inspects X-Forwarded-For. Getting the real address into the log requires a filter that reads the header and writes it there.
Restore real visitor IPs in your IIS logs behind Nginx or HAProxy. Available now for IIS 10 on Windows Server 2016–2025, with a free 14-day trial.
View the product or request a download →