HAProxy Stick Table
Sticky Counters
A stick table is a relational table of connection data (usually IP address of requester: src) to sticky counters.
By default, only three sticky counters are allowed. (This is a compilation option.) They can be referred to as sc0, sc1, and sc2. This means that a connection can only be inserted into three distinct, concurrent sticky tables.
Sticky tables relate connection data to a variety of rates and counters. The most common usecases have a builtin data type and setter/getter functions. There are also general purpose counters and general purpose tags. Up to 100 of these can be used on any sticky table (gpc(0) .. gpc(99)). But similar to the sticky tables themselves, there are convenient names for the first three: gpc0, gpc1, and gpc2.
Usage
Rate Limiting
To track the rate of HTTP requests, allocate an IP address (type ip) sticky table to store http_req_rate(10s) (store http_req_rate(10s)). This data type represents the number of HTTP requests in the last 10 seconds.
backend my_backend stick-table type ip size 1m expire 30s store http_req_rate(10s)
Note also that the table is allocated to track 1 million IP addresses, and entries expire after 30 seconds.
To insert a new connection into the sticky table (which defaulted to sc0), use the track-sc0 directive. Then the current rate can be accessed with sc_http_req_rate(0). (Note the 0 is the sticky table index.)
backend my_backend stick-table type ip size 1m expire 30s store http_req_rate(10s) http-request track-sc0 src http-request deny deny_status 429 if { sc_http_req_rate(0) gt 15 }
It may be necessary to exclude certain requests from rate limiting, such as stylesheets and images.
backend my_backend http-request track-sc0 src if ! { path_end .css .png .jpg }
Abuse Tracking
To monitor and ban abusive clients, use a general purpose counter. Allocate an IP address (type ip) sticky table to store gpc0 (store gcp0).
backend my_backend stick-table type ip size 1m expire 1m store gpc0
Note that these entries expire after 1 minute.
To increment the counter for a client, use the sc-inc-gpc0(0) directive. Then the counter can be accessed with sc_get_gpc0(0). (Again, and in both cases, the 0 is the sticky table index.)
backend my_backend stick-table type ip size 1m expire 1m store gpc0 http-request sc-inc-gpc0(0) if { path_end .php } http-request silent-drop if { sc_get_gpc0(0) gt 0 }
This will cause the abusive clients' programs to hang after making a request to any URL ending in .php, and any new requests made for the next minute will similarly hang.