When hosting an application, you may sometimes have directories in the public directory, which you don’t want to be accessible from the web. 90% of the web will explain how to use a .htaccess file, but this of course doesn’t work when using Nginx. After digging through various obscure posts, I finally managed to find and understand how Nginx roughly handles its configuration. Most importantly of course, how to block access to a directory.
Here’s a basic Nginx config, which processes the request as:
- Capture path ending in .php
- Check the file exists, otherwise fallback to a 404
- Call
php-fpm
- Capture all calls
- Try to load the path as file from disk, otherwise…
- Try to load the path as directory from disk, otherwise…
- Redirect the call to
/index.php
and provide the arguments
server {
listen 80;
listen [::]:80;
server_name example.com;
root /var/www/example.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~* \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
The location matching isn’t done in the order as specified but depends on the used modifier and the predefined processing order. The list goes as follows:
location = /path
– Exact matchinglocation ^~ /path
– Forward matchinglocation ~ /path
– Regular expression case sensitivelocation ~* \.(jpg|png)$
– Regular expression case insensitivelocation /path
– Catch all
Forward matching means that if this block is selected as the best non-regular expression match, no regular expressions will be executed.
For regular expression it’s important to note, that they will trigger for any subsection of a path. For example ~ /site
will match example.com/site
as well as example.com/api/site
. In order to prevent any wrong matches, you may want to use the beginning ^
and end $
modifiers, for example ~ ^/site
will then only match example.com/site
, or ~ \.(jpg|png)$
will only match if the path ends in either .jpg
or .png
.
Armed with this knowledge, we should now be able to adjust the configuration for our desired outcome:
server {
listen 80;
listen [::]:80;
server_name example.com;
root /var/www/example.com;
index index.php index.html;
location ~ ^/(site|content|kirby) {
return 404;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~* \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
With a case sensitive (use ~*
for case insensitive match) we return a 404 for an path starting with either site
, content
or kirby
.
That’s it!
Hope this helps anyone struggling with this setup. And if you’re wondering why I excluded the kirby
directory, it’s because you’ll need this exact configuration when setting up Kirby CMS with Nginx, as the mentioned three directories shouldn’t be publicly accessible.