1

I am totally confused with Nginx server password protection. It does not work as we want. This is my Nginx website configuration block.

server {

    listen 80;
    server_name example.com;
    return 301 http://www.example.com$request_uri;

}

server {

    listen 80;
    server_name www.example.com;

    root /var/www/example.com/public;
    index index.html index.php;

    access_log /var/www/example.com/access.log;
    error_log /var/www/example.com/error.log;

    location ~ /\.svn/* {
        deny all;
    }

    location ~ \.(htaccess|htpasswd) {
        deny all;
    }

    location ~ \.conf$ {
        deny all;
    }

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    # Add trailing slash to */wp-admin requests.
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;

    # Directives to send expires headers and turn off 404 error logging.
    location ~* \.(js|css|xml)$ {
        expires 30d;
        access_log off;
        log_not_found off;
    }

    location ~* \.(?:ico|gif|jpe?g|png|svg)$ {
        expires max;
        add_header Pragma public;
        add_header Cache-Control "public";
        access_log off;
        log_not_found off;
    }

    # W3TC rules
    include /var/www/example.com/nginx.conf;

    # Pass all .php files onto a php-fpm/php-fcgi server.
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;

        fastcgi_buffer_size 128k;
        fastcgi_buffers 256 16k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;

        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME     $fastcgi_script_name;
        fastcgi_pass 127.0.0.1:9000;
    }

    location /search {
        limit_req zone=wpsearch burst=3 nodelay;
        try_files $uri /index.php;
    }

    rewrite ^(.*)/undefined$ /$1 permanent;
    rewrite ^(.*)/undefined/$ /$1 permanent;

    # WordPress SEO Sitemap
    rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
    rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;

}

Now when I password protect wp-admin folder, it does not work exactly as it should. To protect wp-admin folder, I used this...

    location /wp-admin {
        auth_basic "Restricted";
        auth_basic_user_file /var/www/.htpasswd;
    }

Now if I access my website at www.example.com/wp-admin it asks for password. But if I try to access website with this URL www.example.com/wp-admin/index.php then server does not ask for Http Authentication at all and easily let me login and access admin section.

So the problem is, though /wp-admin is password protected, content inside is not. I can still access image files, css, php files sitting inside this folder.

Apart from that I also want to password protect /wp-login.php too, so I added similar block as suggested on WordPress codex but Nginx do not block it at all.

    location /wp-login.php {
        auth_basic "Restricted";
        auth_basic_user_file /var/www/.htpasswd;
    }

Infact almost all examples from WordPress codex Brute Force Attack protection does not work.

Please explain what exactly is the issue here. It appears I am missing something.

Robert hue
  • 125
  • 1
  • 10

1 Answers1

1

A regexp location always takes precedence. This means you should avoid global regexp locations in such cases. You can surely use them as sub-locations in your location configuration. And yes, this means config duplication for everything you want to match with regex locations.

You could possibly avoid matching files you don't need using negative lookbehind in your regular expression (caution, should work but not tested):

location ~ (?<!wp-admin\/).+\.php$ {
    ...
}

The same goes to your images location.

Sure, after you do that, you should add sub-locations in your /wp-admin location for php files and images:

location /wp-admin {
    ...
    location ~* \.(?:ico|gif|jpe?g|png|svg)$ {
        expires max;
        add_header Pragma public;
        add_header Cache-Control "public";
        access_log off;
        log_not_found off;
    }
}

As for wp-login.php, there is an exact location that takes precedence over regex locations:

location =/wp-login.php {
    ...
}

However, you have to specify fastcgi settings in this location as well in order to make PHP-FPM handle the request.

All these tricks are possible and working, but require a lot of copy-paste because regex location will not continue to aggregate settings from other locations and terminate as soon as possible.

In your case you could simply add conditional expression in your server config and avoid all this copy-pasting:

if ( $uri ~ "^\/(wp-admin|wp-login)" ) {
    auth_basic "Restricted";
    auth_basic_user_file /var/www/.htpasswd;
}

However using "if" statement has a lot of possible issues. You have been warned.

jollyroger
  • 1,650
  • 11
  • 19