Any client or intermediate device can insert any HTTP header. And the XFF header isn't formalized in an RFC. So you can get a lot of variation in how different devices handle XFF insertions. Some devices will append the client IP they receive in the IP header to an existing XFF header (if one or more is present already). Others might insert a new XFF header. That header could be inserted before or after existing XFF headers.
BIG-IP inserts a new XFF header at the end of the HTTP headers even when one or more XFF headers exist in the request.
I tested a bit more and the scan iRule doesn't handle requests where the first XFF header value has more than one IP address. Here's an updated version that will parse the first XFF IP address even if there is more than one header and the first header has more than one IP address.
when HTTP_REQUEST {
if { [HTTP::header values x-forwarded-for] ne "" } {
set xff_first_ip [lindex [split [lindex [HTTP::header values x-forwarded-for] 0] ", "] 0]
Breakdown of commands:
[HTTP::header values x-forwarded-for] - Get all XFF header value(s) with each header in a list element
[lindex [HTTP::header values x-forwarded-for] 0] - Get the first XFF header value from the list
[split [lindex [HTTP::header values x-forwarded-for] 0] ", "] - Split the first XFF header value on commas and/or spaces
[lindex [split [lindex [HTTP::header values x-forwarded-for] 0] ", "] 0] - Get the first list element from the above split
}
}
On a tangent here are a few tidbits on the HTTP profile options:
1. If you want to pass on a "trusted" XFF value to a pool member, you can strip out any pre-existing XFF headers and then have LTM insert one using a custom HTTP profile:
profile http sanitized_xff_http {
defaults from http
header insert "x-forwarded-for: [IP::client_addr]"
header erase "x-forwarded-for"
}
2. The checkbox option to insert the XFF header is done before the header to insert action is taken. So if you want to remove any existing XFF headers and then insert a new one, don't use the checkbox option.