Nginx: Locking Down The WordPress Backend By IP Address

I recently looked at the server logs for a new site that had just launched and noticed alot of hits by random IP addresses being made to the WordPress login script. Similar to the excerpt below:

[codesyntax lang=”text”]

182.18.209.4 - - [10/Jun/2013:07:33:26 -0400] "POST /wp-login.php HTTP/1.1" 200 4807 "drinknycity.com/wp-login.php" "Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0"
200.75.126.115 - - [10/Jun/2013:07:36:29 -0400] "POST /wp-login.php HTTP/1.0" 200 4749 "drinknycity.com/wp-login.php" "Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0"
190.37.99.209 - - [10/Jun/2013:07:43:00 -0400] "POST /wp-login.php HTTP/1.0" 200 4749 "drinknycity.com/wp-login.php" "Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0"
85.105.213.253 - - [10/Jun/2013:07:47:18 -0400] "POST /wp-login.php HTTP/1.0" 200 4749 "drinknycity.com/wp-login.php" "Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0"
123.21.234.227 - - [10/Jun/2013:08:06:45 -0400] "POST /wp-login.php HTTP/1.0" 200 4749 "drinknycity.com/wp-login.php" "Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0"
190.26.124.56 - - [10/Jun/2013:08:10:43 -0400] "POST /wp-login.php HTTP/1.0" 200 4749 "drinknycity.com/wp-login.php" "Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0"

[/codesyntax]

Without installing a dedicated plugin to log more details about the requests on the page. At a glance, it appears that automated bots are trying to brute force access to the WordPress backend of the site. If you are like me you most likely only need to access the back end of your WordPress sites from a couple of networks. So I am going to run through the process of locking down access by IP address to wp-login.php under nginx.

The easiest way to limit access to diffent locations of any site under nginx is to create a special location directive under the server definition being used for your site. For the purpose of this example, lets say the nginx server definition is stored in /etc/nginx/sites-available/examplesite.com and contains the following:

[codesyntax lang=”text”]

server {
	listen   80;
	server_name  examplesite.com www.examplesite.com;

	access_log /home/sites/examplesite.com/logs/access_log;
	error_log /home/sites/examplesite.com/logs/error_log;

	if (-f $request_filename) {
		break;
	}
	root   /home/sites/examplesite.com/www;

	location / {
		if (!-e $request_filename) {
			rewrite  ^(.+)$  /index.php?q=$1  last;
		}
		index  index.php index.html index.htm;
	}

	location ~ .php$ {
		client_max_body_size 400m;
		try_files  $uri =404;

		set $php_root /home/sites/examplesite.com/www/;
		fastcgi_pass   127.0.0.1:9000;
		fastcgi_index  index.php;
		fastcgi_param  SCRIPT_FILENAME  $php_root$fastcgi_script_name;	
		include /etc/nginx/fastcgi_params;
	}
}

[/codesyntax]

In order to limit access to the WordPress backend you need to edit the server configuration:

vi /etc/nginx/sites-available/examplesite.com

A location directive is then created for wp-login.php, to limit access to the admin areas of the site. For example:

[codesyntax lang=”text”]

location ~* ^/wp-login.php$ {
	error_page 403 = 404;

    allow 220.20.20.20;
    deny all;

    include fastcgi_params;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/var/run/php-fpm.sock;
}

[/codesyntax]

A reload of the nginx service is then required after saving the changes to make them active.

service nginx reload

This addition will allow visitors from 220.20.20.20 to the wp-login page but reject requests made from other locations. The second line in the location block is not strictly required, but delivering a 404 error rather than a 403 forbidden response helps to hide that the page exists and is just being blocked.