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

nmaynard's avatar
nmaynard
Icon for Altocumulus rankAltocumulus
Nov 15, 2025
Solved

Strict header Insertion

Howdy!

 

Incredibly new to the F5 world, and trying to learn fast after the last F5 SME left.

 

Here's my issue:

Our organization currently has a static page being served up directly on the F5. This is how the former SME implemented the page -

when HTTP_REQUEST {

    switch [HTTP::uri] {

        "/" {

            HTTP::respond 200 content [ifile get webpage_ifile]

        }

        "/mainpage.png" {

            HTTP::respond 200 content [ifile get mainpage_ifile]

        }

        "/favicon.ico" {

            HTTP::respond 200 content [ifile get favicon_ifile]

        }

    }

}

 

It's three files - an html file, the page png, and the fav icon.

 

It was just pinged on a security audit for not having HSTS implemented for this static page. Having read a few of the HSTS implementation guide, I cannot seem to get it to work with this irule. I've tried http_response and http_response_release, and even defining the strict security on the same http::respond line. None of it seems to work.

 

Is there a better way to implement this static page or a way to implement strict security in this situation?

 

Thank you for any help someone can provide!

 

Nick

  • nmaynard​ 

     

    You’re running into a very common issue: when you use `HTTP::respond` in an iRule, you completely generate the full response, meaning F5 won’t automatically add additional headers afterward. 

    So if you want HSTS (or any other header) applied, you must include it directly inside the HTTP::respond command that sends the content.

    Also, you can’t use `HTTP_RESPONSE` or `HTTP_RESPONSE_RELEASE` here because there is no server response coming back—you are short-circuiting the request and generating the response directly on the F5. 

    That means `HTTP_RESPONSE` will never fire.

    To solve this situation

    Add the HSTS header directly in your `HTTP::respond` commands, like this:

    ```
    when HTTP_REQUEST {
        set hsts_header {Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"}

        switch [HTTP::uri] {
            "/" {
                HTTP::respond 200 \
                    content [ifile get webpage_ifile] \
                    "Content-Type" "text/html" \
                    $hsts_header
            }

            "/mainpage.png" {
                HTTP::respond 200 \
                    content [ifile get mainpage_ifile] \
                    "Content-Type" "image/png" \
                    $hsts_header
            }

            "/favicon.ico" {
                HTTP::respond 200 \
                    content [ifile get favicon_ifile] \
                    "Content-Type" "image/x-icon" \
                    $hsts_header
            }
        }
    }
    ```

    This will work as the HSTS header is inserted at the moment the response is generated.
    Since the F5 is creating the response internally, this is the only chance to add the header.


    Make sure this is HTTPS-only

    HSTS must only be applied to HTTPS responses.

    If this iRule applies on an HTTP virtual server, you should either redirect HTTP to HTTPS first or add a protocol check.

    You can do this once with a small TCL variable

    As shown, using a `set` for the header at the top avoids repeating long strings.

    If you want to enforce TLS-only redirect

    Add this at the top:

    ```
    if { [TCP::local_port] == 80 } {
        HTTP::respond 301 Location "https://[HTTP::host][HTTP::uri]"
        return
    }
    ```

     

    I usually prefer using an HTTP Profile instead of iRule

    If your VIP is HTTPS and you want HSTS globally, you can simply:

    1. Go to:

    ```
    Local Traffic → Profiles → HTTP → <your HTTP Profile>
    ```

    2. Set:

    ```
    HSTS = Enabled
    Max Age = 31536000
    Include Subdomains = Checked
    ```

    This is cleaner and doesn't need iRules.

    However, since you're generating the response via `HTTP::respond`, the rule still overrides everything—so adding the header directly in the rule is required unless you switch to standard server responses.


    So here is the summary

    `HTTP::respond` bypasses the normal response pipeline.

    Therefore, you must add `Strict-Transport-Security` in the same command that generates the response.

    Your updated iRule as above will satisfy the security scan.

    let me now for any further assistance.

    F5 Design Engineer

10 Replies

    • nmaynard's avatar
      nmaynard
      Icon for Altocumulus rankAltocumulus

      I've attempted both solutions - activating HSTS through an HTTP profile for the VS and through attempting to define Strict-Transport-Security within the iRule. However, I'm extremely new to this so I'm not sure I'm doing the iRule additions correctly:

      when HTTP_RESPONSE {
          HTTP::header insert Strict-Transport-Security "max-age=16070400; includeSubDomains"
      }

       

      I've included this before and after the iRule, and even tried HTTP_RESPONSE_RELEASE (Which I believe alters the server response?) to no avail. 

  • Hi,

     

    Try this, some options for u

     

    1. If you can change VS profiles: Enable HSTS in the HTTP profile on the HTTPS VS (clean, global, supported). 
    f5-agility-labs-irules.readthedocs.io

    2. If you must keep the static iRule: add the Strict-Transport-Security header as shown in the HTTP::respond examples above.

    3. Ensure HTTP→HTTPS redirect exists so browsers first see the header over TLS and verify with tools like curl -I https://yourhost/ or securityheaders.com.

     

    BR
    Aswin

    • nmaynard's avatar
      nmaynard
      Icon for Altocumulus rankAltocumulus

      I've attempted both solutions - activating HSTS through an HTTP profile for the VS and through attempting to define Strict-Transport-Security within the iRule. However, I'm extremely new to this so I'm not sure I'm doing the iRule additions correctly:

      when HTTP_RESPONSE {
          HTTP::header insert Strict-Transport-Security "max-age=16070400; includeSubDomains"
      }

       

      I've included this before and after the iRule, and even tried HTTP_RESPONSE_RELEASE (Which I believe alters the server response?) to no avail. 

       

      For option 3.) - How would I create an HTTPS redirect? Would that be a separate iRule? I think this VS is only accepting connections over 443, so I would assume that is already HTTPS?

  • You should define the header directly in the HTTP::respond call. The HTTP_RESPONSE and HTTP_RESPONSE_RELEASE events are not triggered in this case.

    Simply append the header to the HTTP::respond calls and adding noserver is always a good idea.

    Reference: https://clouddocs.f5.com/api/irules/HTTP__respond.html

     

    HTTP::respond 200 content [ifile get webpage_ifile] noserver "Strict-Transport-Security" "max-age=16070400; includeSubDomains"

     

    And another well-intentioned hint: Do not try to manage a F5 without the help of a professional. There are many things that can be wrong from a security perspective.

    • nmaynard's avatar
      nmaynard
      Icon for Altocumulus rankAltocumulus

      Would this also change the return I would get with curl -I? Is there even a way to change it since its using HTTP::respond? If I try defining a version or declaring 'noserver', curl still seems to return the HTTP::respond default:

      HTTP/1.0 200 OK

      Server: BigIP

      Connection: Keep-Alive

      Content-Length: 633

       

      Thank you very much for responding!

       

      • HTTP::respond should always returns the same, but you can test it with curl -v to be sure. In this case the HTTP method is unimportant as far I know.

         

         

  • nmaynard​ 

     

    You’re running into a very common issue: when you use `HTTP::respond` in an iRule, you completely generate the full response, meaning F5 won’t automatically add additional headers afterward. 

    So if you want HSTS (or any other header) applied, you must include it directly inside the HTTP::respond command that sends the content.

    Also, you can’t use `HTTP_RESPONSE` or `HTTP_RESPONSE_RELEASE` here because there is no server response coming back—you are short-circuiting the request and generating the response directly on the F5. 

    That means `HTTP_RESPONSE` will never fire.

    To solve this situation

    Add the HSTS header directly in your `HTTP::respond` commands, like this:

    ```
    when HTTP_REQUEST {
        set hsts_header {Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"}

        switch [HTTP::uri] {
            "/" {
                HTTP::respond 200 \
                    content [ifile get webpage_ifile] \
                    "Content-Type" "text/html" \
                    $hsts_header
            }

            "/mainpage.png" {
                HTTP::respond 200 \
                    content [ifile get mainpage_ifile] \
                    "Content-Type" "image/png" \
                    $hsts_header
            }

            "/favicon.ico" {
                HTTP::respond 200 \
                    content [ifile get favicon_ifile] \
                    "Content-Type" "image/x-icon" \
                    $hsts_header
            }
        }
    }
    ```

    This will work as the HSTS header is inserted at the moment the response is generated.
    Since the F5 is creating the response internally, this is the only chance to add the header.


    Make sure this is HTTPS-only

    HSTS must only be applied to HTTPS responses.

    If this iRule applies on an HTTP virtual server, you should either redirect HTTP to HTTPS first or add a protocol check.

    You can do this once with a small TCL variable

    As shown, using a `set` for the header at the top avoids repeating long strings.

    If you want to enforce TLS-only redirect

    Add this at the top:

    ```
    if { [TCP::local_port] == 80 } {
        HTTP::respond 301 Location "https://[HTTP::host][HTTP::uri]"
        return
    }
    ```

     

    I usually prefer using an HTTP Profile instead of iRule

    If your VIP is HTTPS and you want HSTS globally, you can simply:

    1. Go to:

    ```
    Local Traffic → Profiles → HTTP → <your HTTP Profile>
    ```

    2. Set:

    ```
    HSTS = Enabled
    Max Age = 31536000
    Include Subdomains = Checked
    ```

    This is cleaner and doesn't need iRules.

    However, since you're generating the response via `HTTP::respond`, the rule still overrides everything—so adding the header directly in the rule is required unless you switch to standard server responses.


    So here is the summary

    `HTTP::respond` bypasses the normal response pipeline.

    Therefore, you must add `Strict-Transport-Security` in the same command that generates the response.

    Your updated iRule as above will satisfy the security scan.

    let me now for any further assistance.

    F5 Design Engineer

    • nmaynard's avatar
      nmaynard
      Icon for Altocumulus rankAltocumulus

      Apologies on the extremely slow response!

       

      I really appreciate your lengthy response to my post! This was the solution that ended up working in my specific situation. For some reason, blowing away the iRule altogether and adding the headers as a set variable seemed to do the trick. I have had the auditors scan the static page again, and it is now showing the strict header insertion.

       

      Thank you again!