Mar 27, 2026 - For details about updated CVE-2025-53521 (BIG-IP APM vulnerability), refer to K000156741.

Forwarded HTTP Extension Insertion (RFC 7239)

Problem this snippet solves:

Until June 2014, there was no standard HTTP headers to share with web server behind reverse proxy client side request properties like:

  • Client IP Address
  • requested Host header
  • requested protocol (HTTP / HTTPS)
  • Reverse Proxy egress IP address

These informations were injected in following non standard headers

  • Client IP Address : X-Forwarded-For
  • requested Host header : X-Forwarded-Host
  • requested protocol (HTTP / HTTPS) : X-Forwarded-Proto
  • Reverse Proxy egress IP address : Via

The RFC 7239 (Forwarded HTTP Extension) provide a single header which contains all these informations : Forwarded

Here is an examples of Forwarded header :

   Forwarded: for="_gazonk"
   Forwarded: For="[2001:db8:cafe::17]:4711"
   Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
   Forwarded: for=192.0.2.43, for=198.51.100.17

This code insert the Forwarded header with expected values to HTTP request

  • insert for parameter with Client IP address (IPv6 format support with bracket)
  • include in For parameter the client request value of For parameter
  • include in For parameter the client request value of X-Forwarded-For header
  • insert by parameter with IP address (IPv6 format support with bracket)
  • insert proto parameter depending on clientssl profile enabled on VS
  • insert host parameter with HTTP client request Host header

version 1.1 : add route domain id support (remove route domain ID from IP addresses before insert it)

How to use this snippet:

Enable this irule on virtual server an set following variables to 0 or 1 to disable / enable parameter in header:

# Insert "For" parameter
set INSERT_FORWARDED_FOR 1
# Include in "For" parameter values from request Forwarded header
set KEEP_FORWARDED_FOR 1
# Include in "For" parameter values from request X-Forwarded-For header
set CONVERT_XFF_TO_FORWARDED_FOR 1
# Insert "By" parameter
set INSERT_FORWARDED_BY 0
# Insert "Proto" parameter
set INSERT_FORWARDED_PROTO 1
# Insert "Host" parameter
set INSERT_FORWARDED_HOST 0

Code :

when CLIENT_ACCEPTED {
    ############### Configure Which info insert in Forwarded header ###############
    # Insert "For" parameter
    set INSERT_FORWARDED_FOR 1
    # Include in "For" parameter values from request Forwarded header
    set KEEP_FORWARDED_FOR 1
    # Include in "For" parameter values from request X-Forwarded-For header
    set CONVERT_XFF_TO_FORWARDED_FOR 1
    # Insert "By" parameter
    set INSERT_FORWARDED_BY 0
    # Insert "Proto" parameter
    set INSERT_FORWARDED_PROTO 0
    # Insert "Host" parameter
    set INSERT_FORWARDED_HOST 1
    ############### Get HTTP / HTTPS info based on the clientssl profile assigned to the VS ###############
    if { [PROFILE::exists clientssl] == 1} {
        set REQUEST_PROTO "https"
    } else {
        set REQUEST_PROTO "http"
    }
    ############### prepare IPV6 encoding ###############
    if {[set CLIENT_ADDR [getfield [IP::remote_addr] "%" 1]] contains ":"} {set CLIENT_ADDR "\[$CLIENT_ADDR\]"}
}
 
when HTTP_REQUEST {
    set FFOR_HEADER ""
    set FPROTO_HEADER ""
    set FHOST_HEADER ""
    
    ############### Insert "for" parameter ###############
    if {$INSERT_FORWARDED_FOR} {
        set FFOR_HEADER_LIST [list]
        lappend FFOR_HEADER_LIST "for=$CLIENT_ADDR"
        if {$KEEP_FORWARDED_FOR} {
            foreach item [split [HTTP::header "Forwarded"] ";"] {
                if {[string tolower [string trimleft $item]] starts_with "for"} {
                    lappend FFOR_HEADER_LIST "$item"
                    break
                }
            }
        }
        if {$CONVERT_XFF_TO_FORWARDED_FOR && [HTTP::header exists "X-Forwarded-For"]} {
            if {[HTTP::header value "X-Forwarded-For"] contains ","} {
                foreach item [split [HTTP::header value "X-Forwarded-For"] ","] {
                    if {$item contains ":" && !($item contains "\[")} {
                        lappend FFOR_HEADER_LIST "for=\[[string trim $item]\]"
                    } else {
                        lappend FFOR_HEADER_LIST "for=[string trim $item]"
                    }
                }
            } else {
                lappend FFOR_HEADER_LIST "for=[HTTP::header value "X-Forwarded-For"]"
            }
            HTTP::header remove "X-Forwarded-For"
        }
        set FFOR_HEADER [join $FFOR_HEADER_LIST ","]
    }
    ############### Insert "proto" parameter ###############
    if {$INSERT_FORWARDED_PROTO} {
        set FPROTO_HEADER "proto=$REQUEST_PROTO"
    }
    ############### Insert "host" parameter ###############
    if {$INSERT_FORWARDED_HOST} {
        set FHOST_HEADER "host=[HTTP::host]"
    }
    ############### Insert HTTP header only if "by" parameter won't be included          ###############
    ############### Else the whole header will be inserted in HTTP_REQUEST_RELEASE Event ###############
    if {!$INSERT_FORWARDED_BY} {
        HTTP::header remove "Forwarded"
        HTTP::header insert "Forwarded" [join "$FFOR_HEADER $FPROTO_HEADER $FHOST_HEADER" ";"]
    }
}

when HTTP_REQUEST_RELEASE {
    ############### Insert "by" parameter ###############
    if {$INSERT_FORWARDED_BY} {
        if {[set BY_ADDR [getfield [IP::local_addr] "%" 1]] contains ":"} {set CLIENT_ADDR "\[$BY_ADDR\]"}
        set FBY_HEADER "by=$BY_ADDR"
        HTTP::header remove "Forwarded"
        HTTP::header insert "Forwarded" [join "$FFOR_HEADER $FPROTO_HEADER $FHOST_HEADER $FBY_HEADER" ";"]
    }
}

Tested this on version:

13.0
Updated 2 years ago
Version 2.0

6 Comments

No CommentsBe the first to comment