crankshaft
crankshaft

Reputation: 2677

Rate Limiting on NGINX using POST args

I need to apply rate limiting in nginx based on 2 conditions:

  1. the method is POST
  2. the post args includes "_api" key

1. This is unable to detect the _api key in the Post Request:

limit_req_zone "$arg__api" zone=api_zone:10m rate=30r/m;
limit_req zone=api_zone burst=10 nodelay;

2. This can detect the POST method:

map $request_method $limit {
  default "";
  POST $binary_remote_addr;
}

limit_req_zone $limit zone=my_zone:10m rate=20r/m;
limit_req zone=my_zone burst=10 nodelay;

So how can I both detect that it is a POST and that the post contains the _api key ?

UPDATE 1

I just tried detecting the _api key in post with this but it does not work either:

map $request_body $api { 
  "_api" $binary_remote_addr;
  default "";
} 
limit_req_zone $api zone=api_api:10m rate=30r/m;
limit_req zone=api_api burst=10 nodelay;

Upvotes: 4

Views: 3577

Answers (1)

crankshaft
crankshaft

Reputation: 2677

OK, after many hours of research and a LOT of trial and error I have determined that:

$args only contains GET data

$request_body contains POST data but is only available when using:

proxy_pass
fastcgi_pass
uwsgi_pass
scgi_pass

$request_body is a string and when available is not parsed into JSON so you can't do $request_body_api as you can with $args.

I was NOT able to get this working by directly using $request_body in a map:

map $request_body $body {
  default "";
  "~*.*(_api)" $binary_remote_addr;
}
limit_req_zone $body zone=body:10m rate=30r/m;
limit_req zone=body burst=5 nodelay;

And the only way I could find of getting this to work was by adding a cookie to the header containing the post, this works but is not very efficient as if the post request is large it is going to consume a lot of unnecessary resources.

add_header Set-Cookie post_data=$request_body;
map $http_cookie $cook_ {
  default "";
  "~*post_data=.*(\"_api\")" $binary_remote_addr;
}

limit_req_zone $cook zone=cook:10m rate=30r/m;
limit_req zone=cook burst=5 nodelay;

If I have made any incorrect statements/conslusions or if anyone knows of a better method then please let me know, this works but is kind of a hack !

Upvotes: 4

Related Questions