# Rate limiting WebSocket messages for Agents

**URL:** <https://community.f5.com/t/rate-limiting-websocket-messages-for-agents/76958>\
**Category:** Contest Entries\
**Tags:** irules, vegas, appworld2026\
**Created:** [March 11, 2026, 5:27am UTC](https://community.f5.com/t/rate-limiting-websocket-messages-for-agents/76958 "2026-03-11T05:27:59Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![mcabral10](https://avatars.discourse-cdn.com/v4/letter/m/58956e/32.png) [@mcabral10](https://community.f5.com/u/mcabral10)\
**Post date:** [March 11, 2026, 5:27am UTC](https://community.f5.com/t/rate-limiting-websocket-messages-for-agents/76958/1 "2026-03-11T05:27:59Z")

</div>

## Problem

Protecting WebSocket-based AI services from Overload caused by high message rates, temporary spikes via burst control, resource waste from duplicate or repeated messages, aggressive/malicious agents with temporary penalties, and lack of visibility via structured JSON logging.

## Solution

This iRule protects WebSocket endpoints from aggressive or misbehaving AI agents by enforcing message rate limits, burst controls, and duplicate suppression. Each client IP is **allowed up to 40 messages per 10 seconds (rate\_limit / rate\_window)** with a **maximum of 20 messages per second (burst\_limit)**. Duplicate messages within **5 seconds (dup\_ttl) are dropped**, and any client exceeding limits is **temporarily penalized for 60 seconds (penalty\_time) and disconnected**. All violations are logged in **JSON format to an HSL pool** , including timestamp, client IP, event type, message content, and count.

## Impact

For organizations running AI at scale, this is a huge game changer that safeguards availability, performance, and security across potentially thousands of clients simultaneously.

## Code

```tcl
when RULE_INIT {

    # HSL pool for JSON logging
    set static::hsl_pool "syslog_pool"

    # Sliding window rate limit: 40 messages per 10 seconds
    set static::rate_limit 40
    set static::rate_window 10

    # Burst protection: 20 messages per second
    set static::burst_limit 20

    # Duplicate message suppression TTL (seconds)
    set static::dup_ttl 5

    # Penalty/quarantine duration (seconds)
    set static::penalty_time 60

}

# -----------------------------
# Detect WebSocket Upgrade
# -----------------------------
when HTTP_REQUEST {
    if {[string tolower [HTTP::header "Upgrade"]] eq "websocket"} {
        # Nothing required here, IP can be grabbed from client_addr in other events
    }
}

# -----------------------------
# Inspect WebSocket Frames
# -----------------------------
when WS_CLIENT_DATA {
    set payload [WS::payload]
}

when WS_CLIENT_FRAME {

    set ip [IP::client_addr]

    # Open HSL
    set hsl [HSL::open -proto UDP -pool $static::hsl_pool]

    # -----------------------------
    # Check penalty/quarantine
    # -----------------------------
    if {[table lookup "ws_penalty:$ip"] ne ""} {

        # Log JSON event
        set ts [clock format [clock seconds] -gmt 1 -format "%Y-%m-%dT%H:%M:%SZ"]
        set logmsg [string map {\" \\\" \n "" \r ""} $payload]
        set json "{\
\"timestamp\":\"$ts\",\
\"client_ip\":\"$ip\",\
\"event\":\"penalty_block\",\
\"message\":\"$logmsg\",\
\"count\":\"0\"\
}"
        HSL::send $hsl $json

        # Mark violation for disconnect
        table set "ws_violation:$ip" 1 2
        return
    }

    # -----------------------------
    # Sliding window rate counter
    # -----------------------------
    set rate_key "ws_rate:$ip"
    set rate [table incr $rate_key]
    if {$rate == 1} {
        table timeout $rate_key $static::rate_window
    }

    # -----------------------------
    # Burst detection
    # -----------------------------
    set burst_key "ws_burst:$ip"
    set burst [table incr $burst_key]
    if {$burst == 1} {
        table timeout $burst_key 1
    }

    # -----------------------------
    # Duplicate message detection
    # -----------------------------
    set hash [crc32 $payload]
    set dup_key "ws_dup:$ip:$hash"

    if {[table lookup $dup_key] ne ""} {

        # Log duplicate message
        set ts [clock format [clock seconds] -gmt 1 -format "%Y-%m-%dT%H:%M:%SZ"]
        set logmsg [string map {\" \\\" \n "" \r ""} $payload]
        set json "{\
\"timestamp\":\"$ts\",\
\"client_ip\":\"$ip\",\
\"event\":\"duplicate_message\",\
\"message\":\"$logmsg\",\
\"count\":\"$rate\"\
}"
        HSL::send $hsl $json

        WS::frame drop
        return
    }

    # Store this message hash for duplicate detection
    table set $dup_key 1 $static::dup_ttl

    # -----------------------------
    # Rate violation check
    # -----------------------------
    if {$rate > $static::rate_limit || $burst > $static::burst_limit} {

        # Log rate limit exceeded
        set ts [clock format [clock seconds] -gmt 1 -format "%Y-%m-%dT%H:%M:%SZ"]
        set logmsg [string map {\" \\\" \n "" \r ""} $payload]
        set json "{\
\"timestamp\":\"$ts\",\
\"client_ip\":\"$ip\",\
\"event\":\"rate_limit_exceeded\",\
\"message\":\"$logmsg\",\
\"count\":\"$rate\"\
}"
        HSL::send $hsl $json

        # Apply penalty/quarantine
        table set "ws_penalty:$ip" 1 $static::penalty_time

        # Mark violation for disconnect in FRAME_DONE
        table set "ws_violation:$ip" 1 2
        return
    }

}

# -----------------------------
# Disconnect violating clients in valid event
# -----------------------------
when WS_CLIENT_FRAME_DONE {

    set ip [IP::client_addr]

    if {[table lookup "ws_violation:$ip"] eq "1"} {
        WS::disconnect 1000 "Violation occurred"
        table delete "ws_violation:$ip"
    }

}

```
