Search code examples
regexapache.htaccessmod-rewrite

Do not have access to all folders except a specific folder by .htaccess


I have this directory structure:

./project/
├── docs/
├── framework/
├── .git/
├── .gitignore
├── .htaccess
└── web/

Address: http://demo.example.com/project Or http://project.demo.example.com

My project front-end files are in the web path.
I want to block access to all files and folders except the web folder.
Apparently this should be done by .htaccess.
Initially there were solutions using Directory or DirectoryMatch, but these not allowed in .hatccess. So I had to use RewriteRule.
I also do not want it to be associated with the project name.

I found this command:

RewriteRule (docs|framework|\.git|\.gitignore|\.htaccess)(/.*)?$ - [F,NC]

This looks good.
Returns 403 Forbidden for the addresses listed. It also works well for subfolders.
But the main problem is that folders may change. So I decided to change the policy and call it all folders except web.
To do this, I wrote this:

RewriteRule (?!web)(/.*)?$ - [F,NC]

But this does not cover all cases.
In fact it fits all patterns!

For faster testing, you can use this link or this example link.


Solution

  • With Apache you can negate a regex (or expression) by simply prefixing it with ! (exclamation mark).

    For example, to block every URL, except those that start /project/web/, you can use the following in the /project/.htaccess file:

    RewriteRule !^web/ - [F]
    

    Providing the .htaccess file is inside the /project subdirectory then you do not need to specify the project/ prefix, since the URL-path matched by the RewriteRule pattern is relative to the location of the .htaccess file.

    This needs to go near the top of the .htaccess file.


    RewriteRule (?!web)(/.*)?$ - [F,NC]
    

    Alternatively, using a negative lookahead in the regex (NB: Apache uses PCRE, not the "Java 8" flavour of regex).

    You are missing the start of string anchor. But then the slash won't match since the negative-lookahead assertion does not actually match anything (and the URL-path matched by the RewriteRule pattern does not start with a slash).

    You could use the following instead:

    RewriteRule ^(?!web($|/)) - [F]
    

    We don't need to actually match anything, the regex just needs to be "successful" in order to trigger the directive and block the request. For example


    Alternatively, you can use mod_authz_core instead. For example:

    # /project/.htaccess
    
    # Block access to everything
    Require all denied
    

    Create an additional access file in the /web sudirectory:

    # /project/web/.htaccess
    
    # Allow access
    Require all granted
    

    Or, you can do this all in /project/.htaccess with an Apache <If> expression (Apache 2.4). Although this is made a little more complex because it could be accessed by either the project subdomain or subdirectory and we are matching against the entire URL-path. For example:

    # /project/.htaccess
    
    # Block everything except the "/web" subdirectory
    <If "%{REQUEST_URI} !~ m#^(/[^/]+)?/web($|/)#">
        Require all denied
    </If>
    

    The operator !~ does not match the regex.