0

What I have

I have Nginx configured to handle the .php files for all virtual hosts, and everything works fine.

location ~ \.php {
    include snippets/fastcgi-php.conf;
    keepalive_timeout 0;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}

The above sits in snippets/common.conf and is included to every virtual host configuration. The aforementioned fastcgi-php.conf sits next to it.

What I need

For a given virtual host, I need to deny access to all files except /example.php.

What I tried

  1. location ~ ^/example {
        allow all;
    }
    location ~ ^/ {
        allow 123.123.123.123;
        deny all;
    }
    

    The restriction itself works fine, but the PHP file is now returned as a source text, instead of being handled. That means the above somehow overwrites the previous location ~ \.php, even though I haven't even included the .php extension here.

  2. Create common-php.conf

    include snippets/fastcgi-php.conf;
    keepalive_timeout 0;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php7.4-fpm.sock;
    

    Reuse it in common.conf:

    location ~ \.php {
        include snippets/common-php.conf;
    }
    

    And in my actual virtual host configuration:

    location ~ ^/example {
        allow all;
        include snippets/common-php.conf;
    }
    location ~ ^/ {
        allow 123.123.123.123;
        deny all;
        include snippets/common-php.conf;
    }
    

    The problems with the above are:

    • It significantly complicates the setup
    • It seems redundant to configure /example.php twice
    • It doesn't fully work, as I'm getting 403 at e.g. /plugins/Feedback/angularjs/feedback-popup/feedback-popup.directive.html?cb=abcd1234 for some reason. The CMS I'm using is complicated, and for the previous two reasons, I didn't investigate further.

I would like to configure access to the /example.php file without overwriting the existing PHP handler it has attached to it. Is this possible? Or are the includes my only option?

Please let me know if you can think of a better configuration for this task. Thank you!

mehov
  • 568
  • 1
  • 5
  • 14

1 Answers1

0

That is, a very common error is doing something like

location / {
    ... some settings
}
location /admin {
    ... additional settings
}

and expecting that /admin/... request would use a combined settings. It won't, depending on location where the request will be finally served, only one settings set will be applied. However as I already said every request can traverse through several location blocks and some tricks can be done using variables manipulation. A certain variable can get changed several times while request travels through different locations and its actual value can be used in the final location as some directive parameter. This question solution won't use this technique, but it is possible for some other cases.

Many things (e.g. conditional headers) can be implemented using the (very powerful) nginx map feature. An alternative to allow/deny rules can be achieved combining the map and geo blocks to evaluate a variable acting as a flag to indicate should the request be blocked or not (some more generic example can be found here):

map $uri $restricted {
    /example.php     0;
    default          1;
}
geo $deny {
    123.123.123.123  0;
    default          $restricted;
}

Now to make a decision whether request should be blocked or not, you can use the following if block:

if ($deny) { return 403; }

It can be placed either at the server level or at the PHP handler location. However placing it at the server level would be more optimal since it will be executed at the earlier NGX_HTTP_SERVER_REWRITE_PHASE instead of NGX_HTTP_REWRITE_PHASE.

If for some reason you'd want to add the basic authentication instead of IP filtering, it can be achieved using the following trick:

map $uri $restricted {
    /example.php     off;
    default          "Protected area";
}
geo $realm {
    123.123.123.123  off;
    default          $restricted;
}
location ~ \.php$ {
    auth_basic $realm;
    auth_basic_user_file /path/to/.htpasswd;
    ...
}
Ivan Shatsky
  • 2,360
  • 2
  • 6
  • 17