Hey Claude, can you write iRules?

So far I have tested two generative AIs to see how they perform when asked to write iRules.

About two years ago, when ChatGPT was new and hyped, I asked it to write several iRules, from easy to medium difficulty.

In February this year, when everyone was crazy about DeepSeek, I gave it the same task. And I also gave ChatGPT a second chance with the same tasks.

The results were never great. ChatGPT has improved slightly over the years, DeepSeek has failed big time.

 

This time I put Claude 3.7 Sonnet to the same test, as it is known for its strong coding capabilities.

I asked Claude to write 5 different iRules:

 

  1. Write me an iRule that redirects HTTP to HTTPS.
  2. Write me an iRule that rewrites the host header in HTTP Request and Response.
  3. Write me an iRule that will make a loadbalancing decision based on the HTTP Host header.
  4. Write me an iRule that will make a loadbalancing decision based on the HTTP URI header.
  5. Write me an iRule that shows different ASM blocking pages based on the host header. The response should include the support ID.

First task - redirect HTTP to HTTPS

when HTTP_REQUEST {
    # Check if the connection is over HTTP (not HTTPS)
    if { [HTTP::header exists X-Forwarded-Proto] } {
        if { [HTTP::header X-Forwarded-Proto] eq "http" } {
            HTTP::redirect "https://[HTTP::host][HTTP::uri]"
        }
    } elseif { [SSL::cipher version] eq "" } {
        # Direct client connection over HTTP - redirect to HTTPS
        HTTP::redirect "https://[HTTP::host][HTTP::uri]"
    }
}

I prefer to use HTTP::respond 301 over HTTP::redirect, because HTTP::redirect always sends an HTTP 302 (Temporary Redirect) status code.

The check for an existing X-Forwarded-Proto header is interesting because this header is commonly used in environments where traffic passes through proxies or load balancers before reaching the F5 BIG-IP. Without checking the X-Forwarded-Proto header, the F5 would see all traffic as plain HTTP and might create an infinite redirect loop.

Second task - Rewrite host headers

Claude came up with the iRule below.
One might say this solution is a bit too comprehensive, however - it works.
Claude often writes long comments and uses lots of log statements.

when HTTP_REQUEST {
    # Log original host header for troubleshooting
    log local0. "Original Host header: [HTTP::host]"
    
    # Check if we need to modify the host header (can add specific conditions here)
    if { [HTTP::host] contains "old-domain.com" } {
        # Change the host header to the new value
        HTTP::header replace Host "new-domain.com"
        log local0. "Host header modified to: new-domain.com"
    }
    
    # You can add additional rewrite rules for different domains as needed
    # Example:
    # if { [HTTP::host] equals "another-old-domain.com" } {
    #     HTTP::header replace Host "another-new-domain.com"
    # }
}

when HTTP_RESPONSE {
    # Some response headers that might contain the hostname
    set headers_to_check {
        "Location"
        "Content-Location"
        "Refresh"
        "Set-Cookie"
    }
    
    # Loop through headers that might need to be rewritten
    foreach header $headers_to_check {
        if { [HTTP::header exists $header] } {
            set header_value [HTTP::header value $header]
            
            # Replace old domain with new domain in each header
            if { $header_value contains "old-domain.com" } {
                set new_value [string map {"old-domain.com" "new-domain.com"} $header_value]
                HTTP::header replace $header $new_value
                log local0. "Modified $header header from: $header_value to: $new_value"
            }
        }
    }
    
    # Optional: If you need to modify response body as well (more resource intensive)
    # if { [HTTP::header value "Content-Type"] contains "text/html" } {
    #     STREAM::expression "@old-domain.com@new-domain.com@"
    # }
}

 

Third task - Layer 7 loadbalancing based on the Host header

To solve this task, the following iRule was devised by Claude.
Similar to the previous solution, the iRule contaings many comments and makes extensive use of logging.
I would recommend logging less, unless there is a need to debug.
Tracking persistence is a good idea Claude came up with.

when HTTP_REQUEST {
    # Log the incoming host header for troubleshooting
    log local0. "Request received for host: [HTTP::host]"
    
    # Make load balancing decisions based on the Host header
    switch -glob [string tolower [HTTP::host]] {
        "api.example.com" {
            # Send API traffic to the API pool
            pool api_server_pool
            log local0. "Directing to api_server_pool"
        }
        
        "admin.example.com" {
            # Send admin traffic to the admin pool
            pool admin_server_pool
            log local0. "Directing to admin_server_pool"
        }
        
        "static.example.com" {
            # Send static content traffic to CDN or static servers
            pool static_content_pool
            log local0. "Directing to static_content_pool"
        }
        
        "*.dev.example.com" {
            # Send development/staging traffic to development servers
            pool development_server_pool
            log local0. "Directing to development_server_pool"
        }
        
        default {
            # Default pool for all other host headers
            pool default_server_pool
            log local0. "Directing to default_server_pool"
        }
    }
}

# Optional: Track persistence if needed
when HTTP_RESPONSE {
    # You can set up persistence here if needed
    # Example: if { [HTTP::cookie exists "sessionid"] } {
    #    persist uie [HTTP::cookie "sessionid"]
    # }
}

 

Fourth task - Layer 7 loadbalancing based on the URI header

To keep this post short - Claude solved this issue similar to the previous task.

Fifth task - Custom blocking pages in ASM based on the Host header

This is where Claude failed, using the HTTP::host command in the ASM_REQUEST_BLOCKING event, resulting in a TCL error.

A working example for this task would be:

when HTTP_REQUEST { 
   set reqhost [HTTP::host]
}

when ASM_REQUEST_DONE {
   set asm_support_id [ASM::support_id]
}

when ASM_REQUEST_BLOCKING {
   HTTP::header remove Content-Length

   switch -glob [string tolower $reqhost] {
      www.domain01.com {
         set response "<html>
         <head>
        <title>Request Rejected</title>
         </head>
         <body>
            The requested URL was rejected. Please consult with your Domain01 administrator.<br><br>
            Your support ID is: $asm_support_id<br><br><a href='javascript:history.back();'>Go Back</a><br><br>
         </body>
         </html>"
      }
      www.domain02.com {
         set response "<html>
         <head>
         <title>Request Rejected</title>
         </head>
         <body>
            The requested URL was rejected. Please consult with your Domain02 administrator.<br><br>
            Your support ID is: $asm_support_id<br><br><a href='javascript:history.back();'>Go Back</a><br><br>
         </body>
         </html>"
      }
      default {
         set response "<html>
         <head>
         <title>Request Rejected</title>
         </head>
         <body>
            The requested URL was rejected. Please consult with your administrator.<br><br>
            Your support ID is: $asm_support_id<br><br><a href='javascript:history.back();'>Go Back</a><br><br>
         </body>
         </html>"
      }
   }
   ASM::payload replace 0 [ASM::payload length] ""
   ASM::payload replace 0 0 $response
}

 

Conclusion

In April 2025 Claude does a better job writing iRules than ChatGPT and DeepSeek did in February.
That said - generative AIs are advancing at incredible speed. The results of this week might be obsolete tomorrow.
At the time of writing I can say, that Claude is a decent assitant in writing iRules.

Updated Apr 17, 2025
Version 3.0

4 Comments

  • But... but Vibe Coding! :😆

    I think this does illustrate that while AI might understand semantics of of the language you're working with, unless it has a deeper understanding of A) the intricacies of the methods and what's most appropriate for a given workflow and B) the efficiencies and nuances of the protocols being affected, you're better off using it as an assistant for now.  Code validation, workflow efficiencies (thinking merge/debase, commits, code validation) are your better bets.

    At least for now.

    Great post Daniel!

  • Love this series, Daniel_Wolf​ ! I'd like to see advances not only in code quality, but in consideration of security implications (argument injection vulnerabilities in the switch commands) and opinionated style (adding event priorities, etc). Bonus: performance optics as well (contains versus ends_with, log local vs HSL).

  • Thank you all for the positive feedback!

    JRahm​ , in that sense even the VSCode plugin is better. It would warn you with squiggly lines that switch is prone to argument injection or recommend you to use event priorities. Or GenAI to remember me to unset variables... like I never do. :)
    Just sending an iRule to any GenAI and asking it for performance optimization... that would be a great step forward.

    Chase_Abbott​ For me Vibe Coding goes in the same bubble like the Action Figures that appeared on Linkedin last week. Hype stuff...
    ChatGPT has prooven itself to be good at improving my efficiency on the bike. It's quite good at making training plans. Let's see if I can use it for improving other, more IT-related, workflows anytime soon.