Every reverse proxy, load balancer and CDN hides the visitor's address behind its own, and each one hands the original back to you in a slightly different header. This is the reference table: what each vendor sets, whether you have to turn it on, and which header is actually safe to log.
Short answer. Nearly every proxy sets X-Forwarded-For, and it is the one to log: it is the cross-vendor standard and it preserves the whole proxy chain, so the address can be validated against a trust list. Vendor-specific headers like CF-Connecting-IP, True-Client-IP and X-Azure-ClientIP are simpler to parse because they hold a single address, but they carry no chain to validate and they tie your logging to one vendor.
Two different things get called "the client IP" and they are easy to confuse. c-ip is a field in the IIS log. Everything else on this list is an HTTP request header set by something upstream. The whole job is getting the right header value into that one field.
| Name | What it is | Holds |
|---|---|---|
c-ip |
IIS W3C log field, not a header. The "client IP" column in your logs, and the value every SIEM, geo-IP lookup and compliance parser reads. By default IIS fills it with the TCP peer address, which behind a proxy is the proxy. | One address |
X-Forwarded-For |
The de facto cross-vendor standard, set by almost every proxy. Each hop appends the address it saw, so the header preserves the full chain and can be validated against a trust list. | Comma-separated chain |
Forwarded |
The formally standardised version (RFC 7239), carrying for=, proto= and host= in one header. Rarely used in practice; most proxies still send X-Forwarded-For. |
Structured chain |
CF-Connecting-IP |
Cloudflare's own header, always the single address Cloudflare accepted the connection from. Simpler to parse than the chain, but Cloudflare-specific. | One address |
True-Client-IP |
Originally Akamai's header, also emitted by Cloudflare on Enterprise plans. Same meaning as CF-Connecting-IP. If both arrive, they should agree. | One address |
X-Azure-ClientIP |
Set by Azure Front Door: the client address Front Door determined, already stripped of the port. | One address |
X-Azure-SocketIP |
Also Azure Front Door: the raw TCP socket address of the connection. Differs from X-Azure-ClientIP when another proxy sits in front. | One address |
X-ARR-LOG-ID |
Added by IIS Application Request Routing. A per-request correlation GUID for tracing a request across ARR and the backend. It is not a client IP, despite the name suggesting a log value. | GUID |
X-Real-IP |
A convention, not a standard. Commonly set by hand in Nginx configs to carry a single address alongside X-Forwarded-For. | One address |
| Proxy / CDN | On by default | Headers it sets | Notes |
|---|---|---|---|
| Cloudflare | Automatic | X-Forwarded-ForCF-Connecting-IPTrue-Client-IP |
CF-Connecting-IP holds the visitor address on its own. True-Client-IP is an Enterprise-plan feature. Full guide → |
| AWS Application Load Balancer AWS Classic Load Balancer |
Automatic | X-Forwarded-For |
Added for you at layer 7. Nothing to enable. Full guide → |
| AWS Network Load Balancer | Not applicable | None | Operates at layer 4. With target-type instance the original source IP reaches your server intact, so no header is needed, and none is added. |
| Azure Application Gateway | Automatic | X-Forwarded-For |
Appends IP:port, not a bare address. Anything parsing the header has to strip the port. Full guide → |
| Azure Front Door | Automatic | X-Forwarded-ForX-Azure-ClientIPX-Azure-SocketIP |
X-Azure-ClientIP is the visitor address; X-Azure-SocketIP is the address of the socket that connected to the edge. Full guide → |
| F5 BIG-IP (LTM) | Off by default | X-Forwarded-For |
Enable Insert X-Forwarded-For on the HTTP profile attached to the virtual server. Without it, SNAT leaves you with F5 addresses only. Full guide → |
| IIS Application Request Routing (ARR) | Automatic | X-Forwarded-ForX-ARR-LOG-IDX-ARR-SSL |
Preserve client IP in the following header defaults to X-Forwarded-For in the ARR server proxy settings — worth confirming rather than assuming. X-ARR-LOG-ID is a correlation ID, not an address. Full guide → |
| Nginx | Off by default | X-Forwarded-For |
Nothing is forwarded until you add it yourself: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; Full guide → |
| HAProxy | Off by default | X-Forwarded-For |
Requires option forwardfor in the backend or defaults section. Full guide → |
Apache mod_proxy |
Automatic | X-Forwarded-ForX-Forwarded-HostX-Forwarded-Server |
mod_proxy_http adds all three when acting as a reverse proxy. |
| Akamai | Check setting | True-Client-IPX-Forwarded-For |
True-Client-IP is Akamai's original single-value header; the header name itself is configurable in property settings. |
| Fastly | Automatic | Fastly-Client-IPX-Forwarded-For |
Fastly-Client-IP holds the visitor address on its own. |
X-Forwarded-For is not a single address — it's a comma-separated list, and each proxy in the path appends the address it received the connection from. A request through a CDN and then an internal load balancer arrives looking like this:
X-Forwarded-For: 203.0.113.45, 198.51.100.7, 10.0.0.12
Reading left to right: 203.0.113.45 is the visitor, 198.51.100.7 is the CDN edge that spoke to your load balancer, and 10.0.0.12 is the load balancer that spoke to your web server. The leftmost entry is the one you want.
The leftmost entry is also the only one an attacker controls. Anyone who can reach your origin directly can send X-Forwarded-For: 1.2.3.4 and every proxy in front of you will simply append to it, leaving their forged value sitting in first position.
The safe method is to walk the list from the right, discarding addresses you recognise as your own infrastructure, and take the first address that isn't on your trust list. That's the last hop you can actually vouch for. This is what a Proxy Trust List does in X-Forwarded-For for IIS, and it's why the chain matters: a single-value header like CF-Connecting-IP gives you nothing to validate.
Trust the header only as far as you trust the path. Header validation is worth little if attackers can bypass the proxy entirely. Restrict your origin firewall to accept traffic only from your proxy's published ranges, so the header can't arrive from anywhere else.
IP:port rather than a bare address, so naive parsers either fail or record something that isn't an IP.: to strip a port will mangle every IPv6 entry. Split on the last colon only, or parse properly.c-ip and nothing else. The headers arrive and are discarded unless something reads them.X-Forwarded-For. Both carry the visitor's address, but X-Forwarded-For preserves the full proxy chain, so the value can be validated against a trust list rather than taken on faith. It's also the cross-vendor standard, so the same logging setup keeps working if you change CDN. CF-Connecting-IP is single-value and Cloudflare-only.
Because every proxy the request passed through appended the address it saw. The list reads oldest-first: the leftmost entry is the original visitor and each subsequent entry is a hop closer to your server. Multiple addresses mean multiple proxies, not an error.
Yes, trivially, if your origin server can be reached directly. A client can set any value it likes and the proxies in front will append rather than replace it. The defence is two-part: validate the chain from the right against a trust list of your own proxies, and firewall the origin so requests can only arrive through them.
A correlation ID that IIS Application Request Routing adds so a request can be traced across the proxy and the backend. It is not a client IP address; ARR carries the address in X-Forwarded-For, when preserving the client IP is enabled.
No. Microsoft never built X-Forwarded-For support into IIS, so the c-ip field always records the address of whatever opened the TCP connection (your proxy). Restoring the real address needs a filter that reads the header and writes it into the log.
X-Forwarded-For for IIS reads the header your proxy sets and writes the real visitor address into the standard IIS c-ip field, with a Proxy Trust List to validate the chain. Works behind any of the proxies above. IIS 10 on Windows Server 2016–2025.