Forum Discussion

Jeff_C_42204's avatar
Jeff_C_42204
Icon for Nimbostratus rankNimbostratus
Aug 01, 2007

Irule to set expires headers for static conent

Hello Dev Central!

 

 

I have an IIS 6.0 server that is not setting the expires header (cache-control is set) which is causing a potential performance issue.

 

 

I would like to remedy this by adding the expires header for extensions listed in the images data group.

 

 

The following snippet successfuly returns a header "JeffTest" that is 24 hours in the futurein GMT format; so that portion should work just fine for the expires field.

 

 

set cachetime 86400

 

HTTP::header insert JeffTest "[clock format [expr ([clock seconds]+$cachetime)] -format "%a, %d %h %Y %T GMT" -gmt true]"

 

 

The problem I am having is that I need it to only be inserted for static content as defined in my images group. I tried the followign which I hoped would work but did not. Is the http:uri function nto allowed in http response? Or is it possible my brackets/bracing are just wrong?

 

 

when HTTP_RESPONSE {

 

if { [matchclass [HTTP::uri] ends_with $::images]} {

 

set cachetime 86400

 

HTTP::header insert JeffTest "[clock format [expr ([clock seconds]+$cachetime)] -format "%a, %d %h %Y %T GMT" -gmt true]"

 

}

 

 

 

If the http::uri function is not allowed in http_response does that mean I have to set two parts to the irule: the first during http::request which matches class and then sets a flag and a second correponding http_response which checks for that flag and if present inserts the header?

 

 

Thanks ahead of time for the help!

10 Replies

  • Ok I found reference in the following article that http::uri function is not allowed in http_response. It also shows that a session variable can be created to move the data you want into the response. I'm still working on this and it probably is not quite syntactically correct but let me know if you guy see anything glaring or if I seem to be on the right path.

     

     

    http://devcentral.f5.com/wiki/default.aspx/iRules/FileNotFoundHandler.html

     

     

     

    when RULE_INIT {

     

    set OUTBOUND_URI $HTTP_URI

     

    }

     

     

    When HTTP_REQUEST {

     

    set HTTP_URI [HTTP::uri]

     

    }

     

     

    when HTTP_RESPONSE {

     

    if { [matchclass [OUTBOUND_URI] ends_with $::images]} {

     

    set cachetime 86400

     

    HTTP::header insert JeffTest "[clock format [expr ([clock seconds]+$cachetime)] -format "%a, %d %h %Y %T GMT" -gmt true]"

     

    }
  • I was trying to make things a little harder than they needed to be, here is an irule that works:

     

     

    when HTTP_REQUEST {

     

    set HTTP_URI [HTTP::uri]

     

    }

     

     

    when HTTP_RESPONSE {

     

    if {[matchclass $HTTP_URI ends_with $::images]} {

     

    HTTP::header insert Expires "[clock format [expr ([clock seconds]+86400)] -format "%a, %d %h %Y %T GMT" -gmt true]"

     

    }

     

    }
  • bl0ndie_127134's avatar
    bl0ndie_127134
    Historic F5 Account
    You might want to do the check in the request so that you can store just a boolean value instead of the whole uri. It might be a good thing to do if you uri tend to be long.
  • Good thought -- the smaller the stored value the better. I'll edit the rule and do a little bit of testing.
  • This should work for you.

    when HTTP_REQUEST {
      set expire [matchclass [HTTP::uri] ends_with $::images]
    }
    when HTTP_RESPONSE {
      if { $expire == 1 } {
        HTTP::header insert Expires "[clock format [expr ([clock seconds]+86400)] -format "%a, %d %h %Y %T GMT" -gmt true]"
      }
    }

    Make sure you explicitly set expire to 0 for a non-match otherwise for keep-alive connections across the same session, you most likely will get mixed up and have the expire flag set when you aren't wanting it to be. I accomplished this with the single line set statement. If you do an if statement, make sure you have an else setting the value to 0 for a non-match.

    -Joe
  • As more customers are beginning to use YSlow (http://developer.yahoo.com/yslow/ - a Firefox add-on integrated with the popular Firebug web development tool that analyzes web pages and tells you why they're slow based on the rules for high performance web sites) to better develop their sites and applications, I think this topic can become a little bit more important.

    I haven't tried this out yet, but should it work?

     
        when RULE_INIT {  
         300s=5min, 3600s=1hour, 86400s=1day, 604800=1week 
        set expire_content_300 0 
        set expire_content_3600 0 
        set expire_content_86400 0  
        set expire_content_604800 0 
        } 
         
        when HTTP_REQUEST { 
           set expire_content_300 [matchclass [string tolower [HTTP::path]] ends_with ::class_expire_content_300] 
           set expire_content_3600 [matchclass [string tolower [HTTP::path]] ends_with ::class_expire_content_3600] 
           set expire_content_86400 [matchclass [string tolower [HTTP::path]] ends_with ::class_expire_content_86400] 
           set expire_content_604800 [matchclass [string tolower [HTTP::path]] ends_with ::class_expire_content_604800] 
        } 
      
        when HTTP_RESPONSE { 
           if { $expire_content_300 == 1 } { 
              HTTP::header replace "Cache-Control" "max-age=300" 
              HTTP::header replace "Expires" "[clock format [expr ([clock seconds]+300)] -format "%a, %d %h %Y %T GMT" -gmt true]" 
           } elseif { $expire_static_3600 == 1 } { 
              HTTP::header replace "Cache-Control" "max-age=3600" 
              HTTP::header replace "Expires" "[clock format [expr ([clock seconds]+3600)] -format "%a, %d %h %Y %T GMT" -gmt true]" 
           } elseif { $expire_static_86400 == 1 } { 
              HTTP::header replace "Cache-Control" "max-age=86400" 
              HTTP::header replace "Expires" "[clock format [expr ([clock seconds]+86400)] -format "%a, %d %h %Y %T GMT" -gmt true]" 
           } elseif { $expire_static_604800 == 1 } { 
              HTTP::header replace "Cache-Control" "max-age=604800" 
              HTTP::header replace "Expires" "[clock format [expr ([clock seconds]+604800)] -format "%a, %d %h %Y %T GMT" -gmt true]" 
           } else { 
              HTTP::header replace Expires -1 
           } 
        } 
     

    Thanks,

    -Rodrigo
  • Hi Rodrigo,

     

     

    That's a nice rule. You might consider creating a single class which has the extension and an expiry time. You could then use findclass (Click here) to get the expiry value. This would save you from checking four classes for every request. You might also want to use {-1} instead of -1. TCL can be picky about hyphens.

     

     

    Aaron
  • Using getfield to split the path wouldn't work if there is a period anywhere else in the path (like /path/to/my.image.jpg).

    You could add a check to see if the URI is /. It would be a one-off though as you don't want to check if the URI ends with /, it would be if the URI is /.

    To enable use of CMP you can reference the class without the $:: (Click here).

    Any variable set in RULE_INIT is implicitly created as a global variable. To avoid this for expire_content_timeout you could depend on it being set in HTTP_REQUEST.

    Aaron

     
     class class_cacheable_file_types  {     
         "htm 300"     
         "html 300"     
         "css 3600"     
         "js 3600"     
         "bmp 86400"     
         "gif 86400"     
         "jpeg 86400"     
         "jpg 86400"     
         "png 86400"     
         "pdf 604800"     
         "swf 604800"     
         "ico 604800"     
      } 
     

     
     rule irule_manipulate_client_cache { 
              
         when HTTP_REQUEST { 
      
             Set the cache timeout for root requests.  Check that the request is a GET and that there is not a query string. 
            if {[HTTP::method] eq "GET" and [HTTP::uri] eq "/"}{ 
               set expire_content_timeout 300 
            } else { 
                Set the tiemout based on the class entry if it exists for this request 
               set expire_content_timeout [findclass [getfield [string tolower [HTTP::uri]] "." 2] class_cacheable_file_types " "] 
            } 
         } 
      
         when HTTP_RESPONSE {     
            if { $expire_content_timeout ne "" } {     
               HTTP::header replace "Cache-Control" "max-age=$expire_content_timeout, public"     
               HTTP::header replace "Expires" "[clock format [expr ([clock seconds]+$expire_content_timeout)] -format "%a, %d %h %Y %T GMT" -gmt true]"     
            } else {     
               HTTP::header replace "Cache-Control" "private"     
               HTTP::header replace Expires {-1}     
            }     
         } 
      }     
     

    Here's one method for handling files with more than one period:

     
     class class_cacheable_file_types  {     
         ".htm 300"     
         ".html 300"     
         ".css 3600"     
         ".js 3600"     
         ".bmp 86400"     
         ".gif 86400"     
         ".jpeg 86400"     
         ".jpg 86400"     
         ".png 86400"     
         ".pdf 604800"     
         ".swf 604800"     
         ".ico 604800"     
      } 
     

     
     rule irule_manipulate_client_cache { 
              
         when HTTP_REQUEST { 
      
             Set the cache timeout for root requests.  Check that the request is a GET and that there is not a query string. 
            if {[HTTP::method] eq "GET" and [HTTP::uri] eq "/"}{ 
               set expire_content_timeout 300 
            } else { 
                Set the tiemout based on the class entry if it exists for this request. 
               set expire_content_timeout [findclass [string tolower [string range [HTTP::path] [string last . [HTTP::path]] end]] class_cacheable_file_types " "] 
            } 
         } 
      
         when HTTP_RESPONSE { 
            if { $expire_content_timeout ne "" } { 
               HTTP::header replace "Cache-Control" "max-age=$expire_content_timeout, public" 
               HTTP::header replace "Expires" "[clock format [expr ([clock seconds]+$expire_content_timeout)] -format "%a, %d %h %Y %T GMT" -gmt true]" 
            } else { 
               HTTP::header replace "Cache-Control" "private"     
               HTTP::header replace Expires {-1}     
            } 
         } 
      } 
     
  • Colin_Walker_12's avatar
    Colin_Walker_12
    Historic F5 Account
    Definitely a cool example. Feel free to email it in to me and I can get it posted up to the codeshare (or with your permission I'll just grab it from here) as the upload form is undergoing some work right now.

     

     

    Thanks!

     

     

    Colin
  • hi,

     

     

    i don't think there is any roadmap to control intellectual property of an iRules planned. I know it MAY appear on other F5 product but not for iRules