For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Forum Discussion

1000blocks's avatar
1000blocks
Icon for Nimbostratus rankNimbostratus
Mar 07, 2017

Applying lowercase iRule to HTTPS virtual server?

Hi,

 

For SEO reasons, I need to apply force all HTTP requests to my site to be lowercase.

 

The Big-IP configuration for the site is a HTTP virtual server and a HTTPS virtual server (with a client SSL profile).

 

I currently have this iRule below on the HTTP virtual server and this works perfectly.

 

when HTTP_REQUEST {

 

if { [string tolower [HTTP::host]] eq "mysitename.com" } then {

 

HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::uri]]"

 

} else {

 

HTTP::respond 301 noserver Location "https://[getfield [HTTP::host] ":" 1][string tolower [HTTP::uri]]"

 

}

 

}

 

 

This iRule correctly forces HTTP traffic to HTTPS with a 301 response code and forces the URI's to lowercase.

 

E.g. http://www.sitename.com/WHATEVER gets converted to https://www.sitename.com/whatever

 

So this is all good.

 

However my problem is that I can't work out how to do the same lowercase rule for the HTTPS virtual server connections.

 

I currently have this iRule below applied to the HTTPS virtual server and nothing is being converted to lowercase.

 

when HTTP_REQUEST {

 

if { [string tolower [HTTP::host]] starts_with "mysitename.com" } {

 

HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::uri]]"

 

}

 

}

 

 

E.g. https://www.sitename.com/WHATEVER remains as https://www.sitename.com/WHATEVER

 

Is there a way to force lowercase to the HTTPS virtual server as well?

 

Any advice would be appreciated.

 

Thanks in advance!

 

6 Replies

  • Do you have a clientside SSL profile enabled?

     

    If the F5 isn't doing SSL offload or SSL bridging you won't be able to run any HTTP irules to manipulate the data. You will need a HTTP profile as well, but you probably won't be able to attach the iRule without that.

     

  • Hi,

    To redirect only when string is not lower case, you can use the condition

     

    if {[ set uri [string tolower [HTTP::uri]]] ne [HTTP::uri]} {    
        HTTP::respond 301 noserver Location $uri        
    }
    

     

  • here is another Irule to do this

    when HTTP_REQUEST {
    # pool_group1 is the pool name for this virtual server
    set Vuri [ string tolower [HTTP::uri]]
    set Vheader [string tolower [HTTP::host]]
    set Poolmember1 [active_members pool_group1] 
    set Default_site www.mysitename.com
    
    switch $Vheader {
    
        www.mysitename.com {
                            if { [HTTP::uri] ne $Vuri  } {
                            [HTTP::uri] $Vuri
                            }
                            else { pool pool_group1 }
                        }
        default { HTTP::redirect "https://$Default_site$Vuri"}
    }
    }
    

     

  • Ok, looks like I have a working solution now.

    Thanks Stanislas - it's basically your rule blended into with my existing redirection rule.

    So the lowercasing happens as planned, but also every HTTP/HTTPS/WWW/non-WWW combination still gets detected and 301 redirected too.

    Just tested all 10 possible combinations for a page and all good so far.

    1. mysitename.com/page (without HTTP/HTTPS/WWW) --> 301 redirect --> https://www.mysitename.com/page
    2. mysitename.com/PAGE (without HTTP/HTTPS/WWW) --> 301 redirect --> https://www.mysitename.com/page
    3. http://mysitename.com/page --> 301 redirect --> https://www.mysitename.com/page
    4. http://mysitename.com/PAGE --> 301 redirect --> https://www.mysitename.com/page
    5. http://www.mysitename.com/page --> 301 redirect --> https://www.mysitename.com/page
    6. http://www.mysitename.com/PAGE --> 301 redirect --> https://www.mysitename.com/page
    7. https://mysitename.com/page --> 301 redirect --> https://www.mysitename.com/page
    8. https://mysitename.com/PAGE --> 301 redirect --> https://www.mysitename.com/page
    9. https://www.mysitename.com/page --> HTTP/200 --> https://www.mysitename.com/page
    10. https://www.mysitename.com/PAGE --> 301 redirect --> https://www.mysitename.com/page

    Working iRule 1

     

    when HTTP_REQUEST {
       if {[string tolower [HTTP::host]] starts_with "www.mysitename.com" && [string match {*[A-Z]*} [HTTP::uri]]}{
          HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::uri]]"
       } elseif
       {[string tolower [HTTP::host]] starts_with "mysitename.com" && [string match {*[A-Z]*} [HTTP::uri]]}{
          HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::uri]]"
       } elseif
        {[string tolower [HTTP::host]] starts_with "mysitename.com" } {
         HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::uri]]"
       }
    }
    

     

    I'm going to continue to test, but looks good so far.

    If anyone can see any ways that I can optimize this rule and trim some fat off it, please let me know.

    Thanks for everyone's input on this post... much appreciated.

     

  • Hi 1000blocks,

    you may take a look to the two iRules below. Both iRules are using a very fast code path to identify the "good" requests (should be >90% of the total request).

    In addition both iRules are not [string tolower] the entire [HTTP::uri] string, since this approach may break certain applications. Both iRules will therefor [string tolower] only the [HTTP::host] and/or [HTTP::path] information, while keeping possible [HTTP::query] information in the original case.

    iRule1: If is the only sitename served by the virtual server.

     

    when HTTP_REQUEST {
        if { [string tolower "[HTTP::host][HTTP::path]"] equals "www.mysitename.com[HTTP::path]" } then { 
             Allow the request to pass. Should be >90% of the requests. 
            return
        }
         Wrong host or mixed case URL requested. 
        if { [HTTP::query] ne "" } then {
            HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::path]]?[HTTP::query]"
        } else {
            HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::path]]"
        }
    }
    

     

    iRule2: If multiple sitenames are served by the virtual server.

     

    when HTTP_REQUEST {
        switch -glob -- [string tolower [HTTP::host]] {
            "www.mysitename.com" {
                if { [string tolower [HTTP::path]] equals [HTTP::path] } then {
                     Allow the request to pass... 
                } else {
                    if { [HTTP::query] ne "" } then {
                        HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::path]]?[HTTP::query]"
                    } else {
                        HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::path]]"
                    }
                }
            }
            "*mysitename.com*" {
                if { [HTTP::query] ne "" } then {
                    HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::path]]?[HTTP::query]"
                } else {
                    HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::path]]"
                }
            }
            "www.mysitename2.com" {
                if { [string tolower [HTTP::path]] equals [HTTP::path] } then {
                     Allow the request to pass... 
                } else {
                    if { [HTTP::query] ne "" } then {
                        HTTP::respond 301 noserver Location "https://www.mysitename2.com[string tolower [HTTP::path]]?[HTTP::query]"
                    } else {
                        HTTP::respond 301 noserver Location "https://www.mysitename2.com[string tolower [HTTP::path]]"
                    }
                }
            }
            "*mysitename2.com*" {
                if { [HTTP::query] ne "" } then {
                    HTTP::respond 301 noserver Location "https://www.mysitename2.com[string tolower [HTTP::path]]?[HTTP::query]"
                } else {
                    HTTP::respond 301 noserver Location "https://www.mysitename2.com[string tolower [HTTP::path]]"
                }
            }
            default {
                 Further/Unknown HOST-names...
            }
        }
    }
    

     

    Cheers, Kai

  • Hi,

    can you try this irule:

     

    when HTTP_REQUEST {
         Manage HTTP / HTTPS and host values
        switch -glob -- [string tolower [HTTP::host]] {
            "mysitename.com" -
            "mysitename2.com" {
                set RedirLocation "https://www.[string tolower [HTTP::host]]"
            }
            "www.mysitename.com" -
            "www.mysitename2.com" {
                if {[TCP::local_port] equals 80} {
                    set RedirLocation "https://[string tolower [HTTP::host]]"
                } else {
                    set RedirLocation ""
                }
            }
            default {
                set RedirLocation ""
            }
        }
         Manage Path values
        if {[ set path [string tolower [HTTP::path]]] ne [HTTP::path]} {
             Convert path to lowercase to be used in redirect
            HTTP::path $path
            append RedirLocation [HTTP::uri]
        } else {
            append RedirLocation [HTTP::uri]
        }
         redirect if Host, protocol or path require redirect
        if {$RedirLocation ne [HTTP::uri]} {
            HTTP::respond 301 noserver Location $RedirLocation
        }
    }
    

     

    this irule check port, hostname and path values to create a variable RedirLocation with redirect URL (relative if host and scheme are unchanged, absolute else)