Rewriting Redirects

While best practices for virtualized web applications may indicate that relative self-referencing links and redirects (those which don't include the protocol or the hostname) are preferable to absolute ones (those which do), many applications load balanced by our gear still send absolute self-references. This drives a fairly common requirement when proxying or virtualizing HTTP applications: To manipulate any redirects the servers may set such that they fully support the intended proxy or virtualization scheme. In some cases the requirement is as simple as changing "http://" to "https://" in every redirect the server sends because it is unaware of SSL offloading. Other applications or environments may require modifications to the host, URI, or other headers. LTM provides a couple of different ways to manage server-set redirects appropriately.

HTTP profile option: "Rewrite Redirects"

The LTM http profile contains the "Rewrite Redirects" option which supports rewriting server-set redirects to the https protocol with a hostname matching the one requested by the client. The possible settings for the option are "All", "Matching", Node", and "None".

Rewrite Redirects settings for http profile

SettingEffectResulting RedirectUse Case
AllRewrites all HTTP 301, 302, 303, 305, or 307 redirectshttps://<requested_hostname>/<requested_uri>Use "All" if all redirects are self-referencing and the applicaiton is intended to be secure throughout.  You should also use "All" if your application is intended to be secure throughout, even if redirected to another hostname.
MatchingRewrites redirects when the request and the redirect are identical except for a trailing slash. See K14775 .https://<requested_hostname>/<requested_uri>/Use "Matching" to rewrite only courtesy redirects intended to append a missing trailing slash to a directory request.
NodeRewrites all redirects containing pool member IP addresses instead of FQDNhttps://<vs_address>/<requested_uri>If your servers send redirects that include the server's own IP address instead of a hostname.
NoneNo redirects are rewrittenN/ADefault Setting
 

Note that all options will rewrite the specified redirects to HTTPS, so there must be an HTTPS virtual enabled on the same address as the HTTP virtual server.

iRule Options

While these options cover a broad range of applications, they may not be granular enough to meet your needs. For example, you might only want to re-write the hostname, not the protocol, to support HTTP-only proxying scenarios. You might need it to temporarily work around product issues such as those noted in SOL8535/CR89873 . In these cases, you can use an iRule that uses the HTTP::is_redirect command to identify server-set redirects and selectively rewrite any part of the Location header using the HTTP::header command with the "replace" option.

Here's an iRule that rewrites just one specific hostname to another, preserving the protocol scheme and URI as set by the server:

when HTTP_RESPONSE {
  if { [HTTP::is_redirect] }{
    HTTP::header replace Location [string map {&quot;A.internal.com&quot; &quot;X.external.com&quot;} [HTTP::header Location]]
  }
}

Here's one that rewrites both relative and absolute redirects to absolute HTTPS redirects, inserting the requested hostname when re-writing the relative redirect to absolute:

when HTTP_REQUEST {
  # save hostname for use in response
  set fqdn_name [HTTP::host]
}
when HTTP_RESPONSE {
  if { [HTTP::is_redirect] }{
    if { [HTTP::header Location] starts_with &amp;quot;/&amp;quot; }{
      HTTP::header replace Location &amp;quot;https://$fqdn_name[HTTP::header Location]&amp;quot;
    } else {
      HTTP::header replace Location &amp;quot;[string map {&amp;quot;http://&amp;quot; &amp;quot;https://&amp;quot;} [HTTP::header Location]]&amp;quot;
    }
  }
}
 


The string map example could quite easily be adjusted or extended to meet just about any redirect rewriting need you might encounter. (The string map command will accept multiple replacement pairs which can come in handy if multiple hostnames or directory strings need to be re-written -- in many cases you can perform the intended replacements with a single string map command.)

Taking it a step further

As I mentioned earlier, redirects are only one place server self-references may be found. If absolute self-referencing links are embedded in the HTTP payload, you may need to build and apply a stream profile to perform the appropriate replacements. An iRule could also be used for more complex payload replacements if necessary. For the ultimate in redirect rewriting and all other things HTTP proxy, I direct your attention to the legendary ProxyPass iRule contributed to the DevCentral codeshare by Kirk Bauer (thanks, Kirk, for a very comprehensive & instructive example!)

Published May 14, 2008
Version 1.0

Was this article helpful?

9 Comments

  • The HTTP_REQUEST event declaration has an error. The "string map" function is syntactically incorrect. It should be:

     

     

    HTTP::header replace Location "[string map { "http://" "https://" } [ HTTP::header Location]]"

     

  • Note:

     

     

    ]HTTP::header Location[

     

     

    should be

     

     

    [HTTP::header Location]

     

     

    in both examples.

     

     

    Aaron
  • Okay...the error is not the fault of the poster. My post above was modified by the devcentral filter. To correct the syntax revers the square brackets on both sides of the "HTTP::header Location" string towards the end of the line.

     

     

    Weird.
  • maybe this will show the issue?

     

     

    ] HTTP::header Location [

     

     

    should be

     

     

    [HTTP::header Location]

     

     

    Aaron
  • Also missing a space now:

     

     

    if{ [HTTP::header Location] starts_with "/" }{

     

     

    Should be:

     

     

    if { [HTTP::header Location] starts_with "/" }{
  • Lambert_102051's avatar
    Lambert_102051
    Historic F5 Account
    This line:

     

    HTTP::header replace Location "[string map { "http://" "https://" } [ HTTP::header Location]]"

     

     

    Creates a b load/configsync bomb.

     

     

    Should be:

     

    HTTP::header replace Location [string map { "http://" "https://" } [ HTTP::header Location]]

     

  • I've been looking for a method to change absolute redirects into relative redirects. The following does the trick:

     

     

    when HTTP_RESPONSE {

     

    if { [HTTP::is_redirect] } {

     

    Replace absolute redirect with relative redirect

     

    HTTP::header replace Location [regsub {https?://[^/]*/} [HTTP::header value location] "/"]

     

    }

     

    }

     

     

    Basically it looks for every HTTP location starting with "http://" or "https://" and then looks for any character other than "/"/ and replaces it with a single "/"