Learn F5 Technologies, Get Answers & Share Community Solutions Join DevCentral

Filter by:
  • Solution
  • Technology
Answers

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!

0
Rate this Question

Answers to this Question

placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

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        
}
1
Comments on this Answer
Comment made 08-Mar-2017 by Kai Wilke 6544

What a lovely iRule... ;-)

Cheers, Kai

0
Comment made 08-Mar-2017 by Kai Wilke 6544

I've just praised Stanislas iRule^^ So please thank him (by accepting his answer) and not me... ;-)

Cheers, Kai

0
Comment made 08-Mar-2017 by 1000blocks 159

Duh!

Thanks Stanislas - I didn't see your comment earlier.

That looks promising... I will test the rule later today and let you know how it goes.

0
Comment made 08-Mar-2017 by 1000blocks 159

Hi Stanislas.

Just tested that iRule and didn't work.

When it detects an uppercase character in the URI, it causes a redirect loop and site can't load.

So for example with the rule applied...

https://www.mysitename.com/whatever loads fine as normal. https://www.mysitename.com/WHATEVER generates a redirect loop and site doesn't load.

I tested it as a standalone rule by itself with no other iRules applied and also added it as a nested conditional into my existing rule (as below).

E.g. if string tolower HTTP::host starts with "mysitename.com" and string tolower HTTP::uri not equal to HTTP::uri

And both scenarios generated the redirect loop when iRule detects an uppercase character in the URI.

Back to the drawing board for me. :(

Thanks for your help so far though... much appreciated!

0
Comment made 08-Mar-2017 by Stanislas Piron 9067

Did you copy the exact code?

The provided code may not generate loop as the uri in redirect command is lowercase ($uri is previously set with lowercase value of [HTTP::uri])

0
Comment made 09-Mar-2017 by 1000blocks 159

Hi Stanislas,

Yes, copied it exactly but had to specify Location.

As I need to specify the actual location for 'www' to be applied via the 301 response redirect for https://mysitename.com --> https://www.mysitename.com

So, I have Location "https://www.mysitename.com$uri"

Thanks for your help so far - I'm still at the drawing board playing around with elements of your rule and some others, so I think I'm getting close to a working solution.

0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

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.

0
Comments on this Answer
Comment made 07-Mar-2017 by 1000blocks 159

Hi Andrew, thanks for your post.

Yes I have a Client SSL Profile enabled on the HTTPS virtual server.

Note - the problem is just with getting the lowercasing to work, as the other functionality within the same HTTPS virtual server iRule works perfectly fine.

So for example the iRule correctly does the https://sitename.com (without www) to https://www.sitename.com (with www) 301 response redirect that it should... so no problem with that part of the rule or with the rule itself applying.

I just cannot get it to force lowercase to the URI on HTTPS.

:(

0
Comment made 07-Mar-2017 by Andrew Husking 692

What happens if you change starts_with to ends_with

when HTTP_REQUEST {
    if { [string tolower [HTTP::host]] ends_with "mysitename.com" } { 
        HTTP::respond 301 noserver Location "https://www.mysitename.com[string tolower [HTTP::uri]]"
    }
}



*EDIT* Your irule won't cover https://www.mysitename.com only https://mysitename.com

Change your starts with to ends with and I'd say you problem will be solved.
0
Comment made 08-Mar-2017 by 1000blocks 159

Hi Andrew,

No, neither 'equals' or 'ends_with' will work.

Anything other than 'starts_with' will cause redirect looping and break the site.

The sole purpose of the 301 response redirect part of the HTTPS iRule is just to catch non-WWW HTTPS connections and redirect them to the WWW. HTTPS.

So for example...

Site visitor types https://mysitename.com in their browser, they reach the HTTPS site via a single 301 redirect to https://www.mysitename.com

0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

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"}
}

}

0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

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.

0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

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 www.mysitename.com 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

0
Comments on this Answer
Comment made 16-Mar-2017 by 1000blocks 159

Thanks Kai.

Just put your rule into my test Big-IP (ver 12.1.0 HF2) and it throws up some errors.

Image Text

Out of interest- what version Big-IP did you write it on?

0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

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)

0
Comments on this Answer
Comment made 17-Mar-2017 by Kai Wilke 6544

Hi Stanislas,

using a syntax of HTTP::path $path followed by a [HTTP::uri] to to simplify the lower case formating of [HTTP::path] will work, but using the HTTP::request as storage to construct the redirect is not as effective as concentating the new value.

Cheers, Kai

0
Comment made 17-Mar-2017 by Stanislas Piron 9067

You're right. here is the corrected 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]} {
        append RedirLocation $path
        if { [HTTP::query] ne "" } then { append RedirLocation "?[HTTP::query]"}
    }
    # redirect if Host, protocol or path require redirect
    if {$RedirLocation ne ""} {
        HTTP::respond 301 noserver Location $RedirLocation
    }
}
0
Comment made 20-Mar-2017 by 1000blocks 159

Thanks. I'll test out your new rule Stanislas and let you know how it goes.

0