Nyx by Example

Proxy Rate Limiting

Per-IP rate limiting using a sliding window. Counts requests in 1-second windows; returns HTTP 429 when exceeded. For distributed rate limiting across multiple proxy instances, use nyx-kv INCR + EXPIRE.

Code

// nyx-proxy rate limiting — per-IP sliding window
// Rate limiting nyx-proxy — ventana deslizante por IP

var ip_counts: Map = Map.new()
var window_start: Map = Map.new()

fn now_seconds() -> int {
    return time()
}

// Check if an IP has exceeded the rate limit
// Returns true if allowed, false if rate-limited
fn rate_check(ip: String, max_per_sec: int) -> bool {
    let now: int = now_seconds()

    // Initialize window if new IP
    if not ip_counts.contains(ip) {
        ip_counts.insert(ip, "1")
        window_start.insert(ip, int_to_string(now))
        return true
    }

    // Reset window if more than 1 second elapsed
    let start_s: String = window_start.get(ip)
    let start: int = string_to_int(start_s)
    if now - start >= 1 {
        ip_counts.insert(ip, "1")
        window_start.insert(ip, int_to_string(now))
        return true
    }

    // Increment count
    let count_s: String = ip_counts.get(ip)
    let count: int = string_to_int(count_s) + 1
    ip_counts.insert(ip, int_to_string(count))

    return count <= max_per_sec
}

fn main() -> int {
    let max_rps: int = 5  // allow 5 requests per second per IP
    let ip: String = "192.168.1.100"

    // Simulate 8 rapid requests from the same IP
    var i: int = 0
    while i < 8 {
        let ok: bool = rate_check(ip, max_rps)
        if ok {
            print("request " + int_to_string(i + 1) + ": ALLOWED")
        } else {
            print("request " + int_to_string(i + 1) + ": 429 Too Many Requests")
        }
        i = i + 1
    }
    return 0
}

Output

request 1: ALLOWED
request 2: ALLOWED
request 3: ALLOWED
request 4: ALLOWED
request 5: ALLOWED
request 6: 429 Too Many Requests
request 7: 429 Too Many Requests
request 8: 429 Too Many Requests

Explanation

Rate limiting is the first line of defense against both abuse and runaway clients. The fixed-window counter here is the simplest implementation: for each IP, remember when its window started and how many requests have arrived; reset the counter on every new second. It's not perfect — a client hitting the boundary can burst 2x the limit — but it's cheap and good enough for most proxies. In a multi-instance deployment the counters drift because each instance sees only its own traffic; the fix is to keep counts in nyx-kv with INCR + EXPIRE so all instances share one view. For stricter fairness, the token bucket or true sliding window are next steps up.

← Previous Next →

Source: examples/by-example/88-proxy-ratelimit.nx