Forum Discussion

TJ_Vreugdenhil's avatar
Aug 06, 2014

expires header for images iRule v11.x

For all images, we need to implement the expires tag to tell the users’ browser if they already have the image, that they can use that cached version for X hours.

For images, the expires tag should live for 7 days. For css/js files, the expires tag should live for 24 hours.

Does anyone have an example iRule that will do this?

I found these two links which look close :

Would there be a simpler way to do this rather then the examples here? Would both of these work for 11.x?

https://devcentral.f5.com/wiki/iRules.Expires-iRule.ashx https://devcentral.f5.com/questions/irule-to-set-expires-headers-for-static-conent

Would something like this work?

86400 seconds = 24 hours
604800 seconds = 7 days

ltm data-group internal /Common/class_cacheable_file_types{
    records {
     ".css 86400"     
     ".js 86400" 
     ".bmp 604800"     
     ".gif 604800"     
     ".jpeg 604800"     
     ".jpg 604800"     
     ".png 604800"  
     ".ico 604800"  
    }
   type string
}

 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 [class match [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}     
        } 
     } 
  } 

Thanks!

3 Replies

  • John_Alam_45640's avatar
    John_Alam_45640
    Historic F5 Account

    Here is an iRule I wrote a while back, hope it helps:

    I called it suppress_304, I have not touched it in a while so, let me know if it works: These are datagroups you will need:

     class cached_extensions { ".gif"
        ".js"
        ".bmp"
        ".png"
        ".css"
        ".jpg"
        ".JPG"
    }
    class no_cache_directives {
        "private"
        "no"
        "max-age"
    }
    class cached_content_types {
        "java"
        "image"
        "css"
        "plain"
    }
    
    Below is the iRule:
    
    rule suppress_304 {
    when RULE_INIT {
         0 - No, 1 - Yes.
        set static::obey_response_headers 0
        set static::cache_seconds 14400
    }
    
    when HTTP_REQUEST {
    
        set inject_maxage 0
        if { [class match [HTTP::uri] ends_with cached_extensions] } { 
            set inject_maxage 1
         }
    }
    
    when HTTP_RESPONSE {
    
        if {
            not (
                $static::obey_response_headers and 
                (
                [class match [HTTP::header "Cache-Control"] contains no_cache_directives] ||
                [HTTP::header exists "Expires" ]
                    ) 
            )
            and 
            (
                $inject_maxage == 1 || 
                [HTTP::query] eq "" and
                [class match [HTTP::header "Content-type"] contains cached_content_types] 
            )
        } {
            HTTP::header replace Cache-Control "max-age=$static::cache_seconds"
        }
    }
    
    • TJ_Vreugdenhil's avatar
      TJ_Vreugdenhil
      Icon for Cirrus rankCirrus
      This is a nice iRule, but it does not seem to meet the initial requirement: "For images, the expires tag should live for 7 days. For css/js files, the expires tag should live for 24 hours"
  • John_Alam_45640's avatar
    John_Alam_45640
    Historic F5 Account

    I made the expiration duration dynamically assigned from the datagroup value field as your requirements show.

    I also adjusted how the extension is obtained to make a more accurate decision. I did so by inspecting the [URI::basename [HTTP::uri]] rather than the entire [HTTP::uri] which could contain a query and parameters. Even though css and image links usually do not contain queries, it does not hurt to be accurate.

    NOTE: I did not test the changes I made. If you can test and let us know it would be great.

    ltm data-group internal /Common/class_cacheable_file_types {
        records {
         ".css 86400"     
         ".js 86400" 
         ".bmp 604800"     
         ".gif 604800"     
         ".jpeg 604800"     
         ".jpg 604800"     
         ".png 604800"  
         ".ico 604800"  
        }
       type string
    }
    
    ltm data-group internal /Common/no_cache_directives {
        records {
        "private"
        "no"
        "max-age"
        }
    }
    
    ltm data-group internal /Common/cached_content_types {
        records {
        "java"
        "image"
        "css"
        "plain"
        }
    }
    
    rule suppress_304 {
    when RULE_INIT {
         0 - No, 1 - Yes.
        set static::obey_response_headers 0
    
    }
    
    when HTTP_REQUEST {
    
        set inject_maxage 0
        set cache_seconds [class lookup [URI::basename [HTTP::uri]] ends_with /Common/class_cacheable_file_types ]
        if { not ($cache_seconds equals "") } { 
            set inject_maxage 1
        }
    }
    
    when HTTP_RESPONSE {
    
        if {
            not (
                $static::obey_response_headers and 
                (
                [class match [HTTP::header "Cache-Control"] contains /Common/no_cache_directives] ||
                [HTTP::header exists "Expires" ]
                    ) 
            )
            and 
            (
                $inject_maxage == 1 || 
                [HTTP::query] eq "" and
                [class match [HTTP::header "Content-type"] contains /Common/cached_content_types] 
            )
        } {
            HTTP::header replace Cache-Control "max-age=$cache_seconds"
        }
    }