I am trying to protect Drupal running on Nginx from simple DDoS with limit_conn and limit_req. But I encountered some strange behavior with inheritance of limit_conn
directive which I cannot explain.
I have reduced my nginx (1.8.0) config to this bare minimum, which shows the problem:
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
server_name test.dev;
root /var/nginx/drupal; ## <-- Your only path reference.
#Allow not more than 10 simultaneous connections from one address.
limit_conn perip 10;
location / {
#limit_conn perip 1;
# This is cool because no php is touched for static content
try_files $uri @rewrite;
}
location @rewrite {
# Clean URLs are handled in drupal_environment_initialize().
rewrite ^ /index.php;
}
location ~ \.php$ {
#Allow not more than 1 simultaneous *connection_to_PHP* from one address.
limit_conn perip 1;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi.conf;
fastcgi_intercept_errors on;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
}
As you see, I want to limit the number of simultaneous connections to 10 for all requests and to 4 for the php backend. (In this example I have modified the 4 connection to 1, so it would be easier to trigger)
The Nginx documentation at http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html#limit_conn states that:
These directives are inherited from the previous level if and only if there are no
limit_conn
directives on the current level.
But something strange happens in my tests, it looks like nginx is ignoring the directive limit_conn
inside the location ~ \.php$
block:
- When I test this config with 5 simultaneous connection
ab -n 100 -c 5 http://test.dev/
, no blocking happens. As soon as I raise the
limit to 11-c 11
nginx starts to block requests. - If I modify the global connection limit from 10 to 5, nginx limit more than 6 connections (
-c 6
) - it looks like the directive inlocation ~ \.php$
block is ignored. - If I delete the
conn_limit
directive at server level, the directive inlocation ~ \.php$
block, suddenly starts to work! - Even more confusing: if I add the
conn_limit
directive tolocation /
block, it correctly overwrites the global one!
Maybe the problem is in try_files
directive or multiple redirects? I would be very thankful, if someone could explain, why the conn_limit
directive is not being overwritten, like expected.