Some time ago I had the requirement to limit the number of requests per IP to a CouchDB-instance. I couldn’t find an option for CouchDB to achieve that, but as communication with CouchDB is based on HTTP-requests I thought that it should be possible to use nginx as reverse proxy with rate-limiting capabilities for CouchDB. The CouchDB-wiki lists some basic steps how to use nginx as reverse proxy, but rate limiting isn’t mentioned there.
Beside the actual rate limiting I noticed that nginx won’t work, because it doesn’t support chunked request-bodies out of the box, which CouchDB seems to rely onto. But to the rescue there is a nginx module called ngx_chunkin, which adds chunkin support to nginx. To get that module in Debian you need to install nginx-extras from the repository (available since squeeze-backports). The configuration of that module is straight forward as described on the wiki page. All you have to do is to put the following code snippet into the server-context of your nginx configuration:
chunkin on;
error_page 411 = @my_411_error;
location @my_411_error {
chunkin_resume;
}
Having solved that issue, doing the actual rate limiting was the next step. Therefore nginx ships the module ngx_http_limit_req_module. All I had to do was to configure a zone for the limited requests in the http-context of the configuration:
limit_req_zone $binary_remote_addr zone=couchdb_write:10m rate=10r/s;
and the logic what to rate limit (writing requests in my case) into the server-context:
location / {
# POST, PUT, DELETE ... requests will get rate limiting
if ($request_method !~* GET) {
rewrite ^(.*)$ /throttled$1 last;
}
proxy_pass http://127.0.0.1:5985;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /throttled {
internal;
limit_req zone=couchdb_write burst=10000;
rewrite /throttled(.*) $1 break;
proxy_pass http://127.0.0.1:5985;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
So if you put together the chunkin support and the rate limiting, you’ll get the following piece of configuration for a server which does rate limiting of writing CouchDB-requests using nginx:
limit_req_zone $binary_remote_addr zone=couchdb_write:10m rate=10r/s;
server {
listen 5984;
chunkin on;
error_page 411 = @my_411_error;
location @my_411_error {
chunkin_resume;
}
location / {
# POST, PUT, DELETE ... requests will get rate limiting
if ($request_method !~* GET) {
rewrite ^(.*)$ /throttled$1 last;
}
proxy_pass http://127.0.0.1:5985;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /throttled {
internal;
limit_req zone=couchdb_write burst=10000;
rewrite /throttled(.*) $1 break;
proxy_pass http://127.0.0.1:5985;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
I really enjoyed figuring that out, because it was a lot of fun to be able to use a great tool (nginx) to extend another great tool (CouchDB), because they’re using the same protocol (HTTP).