skip to Main Content

I’m trying to enable HTTP Basic Authentication for all URLs except a few, but no matter what I try, I either get auth for all non-static URLs or no auth for all.

My config:

server {
        listen 80;
        server_name dev.website.com *.dev.website.com;

        return 301 https://dev.website.com;
}

server {
        listen 443 ssl;
        include snippets/fastcgi-php-controller.conf;

        server_name dev.website.com;
        root /var/www/website/web/;

        ssl_certificate     /etc/ssl/localcerts/website/dev/website.crt;
        ssl_certificate_key /etc/ssl/localcerts/website/dev/website.key;

        auth_basic_user_file /var/www/website/web/.htpasswd;
        auth_basic "Restricted Access!";

        location /just_a_simple.html {
                try_files /static$uri =404;
        }
        location / {
                rewrite ^ /index.php;
        }
        location /static {
                try_files $uri =404;
        }
        location ~ ^/en/(url1|url2) {
                auth_basic off;
                rewrite ^ /index.php;
        }
}

I’m trying to:

  1. have the just_a_simple.html file served as static, unprotected and it works
  2. have everything in /static served as static, unprotected and it works
  3. have /en/url1 and /en/url2 passed off to FPM, interpreted as PHP, unprotected and it doesn’t work, it’s protected
  4. have all other URLs passed off to FPM, interpreted as PHP, protected and it works

How can I make point 3 above work and achieve all 4 requirements?

snippets/fastcgi-php-controller.conf

location ~ .php$ {
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        try_files $uri /index.php;
        set $path_info $fastcgi_path_info;
        fastcgi_param PATH_INFO $path_info;
        fastcgi_index index.php;
        include fastcgi_params;

        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}

2

Answers


  1. Have you tried adding this to the location blocks you want to allow?

        auth_basic off;
        allow all;
    

    For example:

            location /just_a_simple.html {
                    auth_basic off;
                    allow all; # Allow all to see content
                    try_files /static$uri =404;
            }
    
    Login or Signup to reply.
  2. You are right about request processing phases (described in the development guide). Your rewrite directive will be executed at the NGX_HTTP_REWRITE_PHASE while basic_auth one register its handler at the later NGX_HTTP_ACCESS_PHASE. If you’d need to do an opposite thing, protect the /en/url1 and /en/url2 URIs leaving all the others unprotected, you’d have an option to use a try_files directive to jump to the PHP handler location at the even more later NGX_HTTP_PRECONTENT_PHASE:

    location ~ ^/en/(url1|url2) {
        auth_basic "Restricted Access!";
        auth_basic_user_file /var/www/website/web/.htpasswd;
        try_files "" /index.php$is_args$args;
    }
    

    However this is not an option for your particular case. What you can do instead is to rely on the $request_uri built-in nginx variable, which is always contains an original non-normalized request URI and does not get changed during internal URI rewrites (in is the normalized $uri one that does). Usually you have at least two options:

    • use an if block to conditionally set the auth_basic directive parameter:
    location ~ .php$ {
        set $realm "Restricted Access!";
        if ($request_uri ~ ^/en/(url1|url2)) {
            set $realm off;
        }
        auth_basic $realm;
        auth_basic_user_file /var/www/website/web/.htpasswd;
        ...
    }
    

    However since you have a customized try_files directive inside your PHP handler location, it won’t be an option here since try_files does not get inherited into the virtual nested locations created by if directive when used in the location context. Do you remember that if is evil? So we should either move the $realm variable evaluation one level up to the server context (thus being executed at the NGX_HTTP_SERVER_REWRITE_PHASE):

    set $realm "Restricted Access!";
    if ($request_uri ~ ^/en/(url1|url2)) {
        set $realm off;
    }
    location ~ .php$ {
        auth_basic $realm;
        auth_basic_user_file /var/www/website/web/.htpasswd;
        ...
    }
    

    or better go straight to the second option:

    • use a map block to evaluate the auth_basic directive parameter:
    map $request_uri $realm {
        ~^/en/(url1|url2)  off;
        default            "Restricted Access!";
    }
    server {
        ...
        location ~ .php$ {
            auth_basic $realm;
            auth_basic_user_file /var/www/website/web/.htpasswd;
            ...
        }
    }
    

    The second option will be somewhat more optimal since the $realm variable will be evaluated only when it needs to be.


    P.S. Dealing with PATH_INFO inside your PHP handler makes no sense; you can read here why is it.

    P.P.S. Did I mention you don’t need that location ~ ^/en/(url1|url2) { ... } location in order for this to work?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search