When securing web directories in Nginx, a common oversight occurs when trying to block access to both a parent directory and its contents. The configuration snippet shows a typical case where the main directory /testdir
is blocked, but files matching specific patterns (*.jpg
and *.txt
) remain accessible due to separate location matching.
Nginx evaluates location blocks in this order:
- Exact string matches (
location = /path
) - Regular expression matches (case-sensitive
~
or case-insensitive~*
) - Prefix matches (
location /path
)
In the given configuration, the regex pattern for image/text files takes precedence over the prefix match for /testdir
.
Here are three effective ways to properly restrict access:
Method 1: Nested Location Blocks
location /testdir {
deny all;
return 404;
location ~* \\.(jpg|txt)$ {
deny all;
return 404;
}
}
Method 2: Negative Lookahead Regex
location ~* ^/testdir/.+\\.(jpg|txt)$ {
deny all;
return 404;
}
location /testdir {
deny all;
return 404;
}
Method 3: Using the internal
Directive
location /testdir {
internal;
root /var/www/site;
}
After implementing any solution:
- Test with
curl -I http://yoursite.com/testdir
- Test with
curl -I http://yoursite.com/testdir/file.jpg
- Reload Nginx:
sudo nginx -s reload
For more granular control:
location /testdir {
allow 192.168.1.0/24;
allow 10.0.0.1;
deny all;
location ~* \\.(jpg|txt)$ {
allow 192.168.1.0/24;
allow 10.0.0.1;
deny all;
}
}
- Not considering nested location precedence
- Forgetting to test all file types in the directory
- Overlooking symbolic links that might bypass restrictions
- Not accounting for case sensitivity in file extensions
When implementing directory restrictions in Nginx, many developers encounter a common pitfall where location matching rules can override directory-level restrictions. In the given configuration:
location ~* ^.+\\.(jpg|txt)$ {
root /var/www/site;
}
location /testdir {
deny all;
return 404;
}
The issue occurs because Nginx processes regular expression locations (with ~ or ~*) before prefix locations. Even though /testdir is restricted, files matching the regex pattern still get served.
Here's the proper way to implement comprehensive restrictions:
location /testdir {
deny all;
return 404;
location ~* \\.(jpg|txt)$ {
deny all;
return 403;
}
}
For more complex scenarios, consider this regex-based solution that catches all requests to the directory:
location ~* ^/testdir(/|$).* {
deny all;
return 404;
}
Remember these key points when implementing directory restrictions:
- Always test with different file types and paths
- Consider using 403 instead of 404 for security reasons
- Check for conflicting location blocks in other config files
Here's a production-ready example that handles various scenarios:
location ~ ^/protected(/|$) {
deny all;
return 403;
location ~* \\.(jpg|png|txt|pdf)$ {
deny all;
return 403;
}
location ~ /\\.ht {
deny all;
return 403;
}
}