HAProxy

Rate limit

Reference syntax

Stick table naming

  • Put on frontend/listen: No name, only one nd only used in that section
  • Put on backend: name of the table = name of the backend. Can just create a dummy backend (without any server) to declare a table.

By source IP

stick-table  type ip ...
http-request track-sc0 src

track-sc0 increase counter index 0. track-sc counter 1, and so on

By URL parameter

stick-table  type string ....
acl          has_token url_param(token) -m found 
acl          exceeds_limit url_param(token),table_http_req_rate() gt ....
http-request track-sc0 url_param(token) unless exceeds_limit
http-request deny deny_status 429 if !has_token or exceeds_limit

By request header

....
http-request track-sc0 req.hdr(Host)
...

By URL

stick-table  type binary  len 20  size 100k  expire 10s  store http_req_rate(10s)

# Increase counter
# hash of (Host header + URL path + src IP)
http-request track-sc0 base32+src

# Lookup rate limit per URL stored in a map file, which is a text file of following
#  /url/a  10
#  /url/b  20
#  /url/c  30
# If not found, use default value 20
#
http-request set-var(req.rate_limit)  path,map_beg(/etc/haproxy/rates.map,20)

# Lookup the current counter from the stick table
http-request set-var(req.request_rate)  base32+src,table_http_req_rate()

# Math: req.rate_limit - req.request_rate
acl rate_abuse var(req.rate_limit),sub(req.request_rate) lt 0   

http-request deny deny_status 429 if rate_abuse

Example

frontend ...
    ...

    # SLIDING WINDOW
    # storing:
    #   - a counter of average number of requests over 10 seconds. Multiple counters can be stored
    #   - per every IPv4 address
    #   - maximum 100x2^10 IPs. Old entries will be remored once full
    #   - unused IPs will be removed after 30 seconds
    stick-table type ip size 100k  expire 30s  store http_req_rate(10s)

    # enables tracking of sticky counters from current request
    #  - src: add client IP to the table
    #  - increase counter index 0 (src0)
    http-request track-sc0 src

    # get the first counter from the stick table (sc_http_req_rate(0), multiple counters can be stored)
    # if more than 20 requests, deny
    http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 }
    ...