One of the cool new features in Web Server 7.0 is the check-request-limits SAF. In a nutshell, it can be used to selectively track request rates and refuse requests if limits are exceeded. While its primary purpose is to help protect against denial of service attacks consisting of high request rates, it is also useful in other scenarios where limiting request counts is of interest.
Here’s the simplest possible invocation:
PathCheck fn="check-request-limits" max-rps="10"
This instructs the SAF to track the average requests per second (rps) and refuse client connections if the average rps exceeds 10rps. The average rate is recomputed every 30 seconds (default interval) based on the number of requests received in those past 30 seconds. If the average rps exceeds 10, all subsequent requests are rejected with HTTP response code 503 (service unavailable), the default rejection response. Requests will continue to be rejected until the average rps falls below the threshhold of 10. Since the average is only recomputed once per interval, this means it’ll be at least one interval before normal service resumes. Naturally, these defaults can all be changed. The following invocation is equivalent to the prior one but shows the default values explicitly:
PathCheck fn="check-request-limits" max-rps="10" interval="30" continue="threshhold" error="503"
The other possibility for the continue option is “silence”. If set, the incoming request count must fall to zero (for an entire interval) before normal service resumes. You can use this if you want to force the offending requests to truly “go away” before allowing any more to be serviced.
Now, in most cases one would not use a line such as the above in a real server because it is tracking all requests globally (for that web server process) and that is overly heavy handed unless you really want to limit the entire server to such a low average request rate. Perhaps if it is a home server, but not in most cases.
You may have read about the server variables and <If> tag also introduced in Web Server 7.0. Let’s use some of those capabilities to make the request limiting more interesting:
PathCheck fn="check-request-limits" max-rps="10" monitor="$ip"
The “monitor” parameter is optional but in nearly every case you will want to give it a value. It instructs check-request-limits to track request statistics using separate counters for each monitored value. In the example above, separate stats will be kept for every client IP ($ip expands to the client’s IP) making a request to the server. That’s more like it! Now, any client which exceeds my set limit (10rps) will be refused service but all other clients continue to experience normal operation.
You can use any of the supported server variables as the value of “monitor”, of course. Another interesting one might be $uri:
PathCheck fn="check-request-limits" max-rps="10" monitor="$uri"
Here, instead of setting limits for each client, we set the limit for each URI on the server. Perhaps some areas of your server are harder hit and you wish to limit use of those while allowing normal servicing of other areas? The above directive will accomplish that.
In fact you can combine variable as well. This is also legal:
PathCheck fn="check-request-limits" max-rps="10" monitor="$ip:$uri"
Here the SAF will limit only specific clients which request the same URI(s) too frequently (over 10rps, that is) but all other URIs for those clients and all other clients continue to be serviced normally. Cool!
This functionality is fairly flexible so experiment with it for a bit. While these examples will get you started, I’ll describe a few other scenarios later on.
Here are links to relevant parts of the documentation: