You can achieve this by using internal locations for the hidden folders or files you'd like to protect and a way to check if your hashed code allows access to your files or not.
A direct access to your hidden files (e.g. /protected/folder1/folder2/file.pdf) is not allowed by Nginx as this location has been marked as internal. But your script can redirect to this location with the special header X-Accel-Redirect.
So you can let Nginx do what it can do best, deliver data and your script checks only if access is allowed or not.
Below you can see a simple example for this.
The folder /data contains public content (e.g. public images). Not public images were stored in a different folder (outside htdocs) and provided via location /protected_data. This location has an alias to the folder containing the protected images and a directive internal. So this is not accessable from outside.
In the PHP script I did at first a check if the protected file exists. This may be a security issue but usually checking the user rights is more cost expensive (time consuming) than a simple file_exists. So if security is more important than performance you can switch the order of the checks.
Nginx server config:
...
root /var/www/test/htdocs;
location / {
index index.php index.htm index.html;
}
location /data {
expires 30d;
try_files $uri /grant-access.php;
}
location /protected_data {
expires off;
internal;
alias /var/www/test/protected_data;
}
location ~ \.php$ {
if (!-e $request_filename) {
rewrite / /index.php last;
}
expires off;
include fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_read_timeout 300;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
access_log /var/log/nginx/access.log combined;
}
...
PHP script:
<?php
// this is the folder where protected files are stored (see Nginx config alias directive of the internal location)
define('PROTECTED_FOLDER_FILESYSTEM', '/var/www/test/protected_data');
// this is the url path we have to replace (see Nginx config with the try_files directive)
define('PROTECTED_PUBLIC_URL', '/data');
// this is the url path replacement (see Nginx config with the internal directive)
define('PROTECTED_INTERNAL_URL', '/protected_data');
// check if file exists
$filename = str_replace(
PROTECTED_PUBLIC_URL .'/',
'/',
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
);
if (!file_exists(PROTECTED_FOLDER_FILESYSTEM . $filename)) {
http_response_code(404);
exit;
}
// check if access is allowed (here we will use a random check)
if (rand(1,2)==1) {
// grant access
header('X-Accel-Redirect: ' . PROTECTED_INTERNAL_URL . $filename);
} else {
// deny access
http_response_code(403);
}