How to Properly Restrict Directory Access in Nginx: Blocking Files and Subdirectories


2 views

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:

  1. Exact string matches (location = /path)
  2. Regular expression matches (case-sensitive ~ or case-insensitive ~*)
  3. 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:

  1. Test with curl -I http://yoursite.com/testdir
  2. Test with curl -I http://yoursite.com/testdir/file.jpg
  3. 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;
    }
}