2

Here is my website structure (root):

private
public
.htaccess
index.php

The htaccess file contains:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /index.php [L]

Having this said, all URLs point to index.php

In the private folder I also have a .htaccess file which contains:

Order Deny,Allow
Deny from all

The problem is that http://www.example.com/private/ throws 403 and I don't want this. I may want to use http://www.example.com/private/ location to show something on my website.

I must mention that I don't want to touch the .htaccess file from the private folder in any way.

So, I'm looking for a way to set some rules in the main .htaccess file.

How do I do that? Is it possible?

Edit: More about my problem.

Imagine a CMS with a public folder and a private folder. The private folder doesn't even belong to the root webserver. It should stay out of public_html, www or other public folders. Well, talking about a CMS, not all users understand this, and they may still keep the private folder in public_html.

Having this said, the private folder should contain a htaccess file which Deny's access to the files through the browser (example www.example.com/private/internal_script.php).

But I still want to be able to serve content in the browser for www.example.com/private/ location.

Right now I can serve what content I want on any URL. This is because of the main htaccess file which let me choose what to show using PHP. But, I still can't serve content to URL www.example.com/private/, because this points to the private directory which contains the htaccess with Deny access.

www.example.com/hello/ can be category, an article etc

www.example.com/awesome/ can be category, an article etc

www.example.com/private/ can't be nothing. I get 403...

4
  • 1
    "The problem is that .../private/ throws 403 and I don't want this" - So, what do you want to happen for this request? You can't block a directory and not block a directory. I assume you want to allow access to specific files? But access to the directory itself, ie. .../private/ would still be blocked (with a 403)?
    – MrWhite
    Jan 15 at 20:33
  • I echo @MrWhite's question: What exactly is the use case here? "Deny from all" means literally that: "Deny" access to everything in that directory to "all" -- which is to say that apache will never serve any of the files within that directory. If you want some of the files to be served some of the time, you'll need to look at something like creating passwords or managing access by IP address. But without knowing what you're actually trying to do, there isn't really anything to say except what you want can't be done. As stated above: You can't block a directory and not block a directory.
    – Aaron
    Jan 15 at 21:54
  • "I don't want to touch the .htaccess file from the private folder in any way." - is there a reason for this? Can the /private subdirectory not be moved or renamed? Since the intention would seem to be to block the contents of this directory from all HTTP access, is there a reason why it needs to be called /private in the public web space?
    – MrWhite
    Jan 16 at 2:50
  • 1
    @MrWhite I've just added more details about my problem. I hope is more clear what I'm trying to do.
    – Andrei
    Jan 16 at 3:20
2

This is technically possible, however, there is another "preferred" solution for "a CMS with a public folder and a private folder" (see below).

To summarise the requirements:

  1. Allow requests for /private/ or /private/<something> (where <something> is not a physical file in the /private subdirectory) to be routable through the front-controller (ie. /index.php in the root). Just like any other (non-file) URL.
  2. Any HTTP requests for /private/<actual-file> need to be blocked (403 Forbidden), as they currently are. (UPDATE: Although this is not required it seems and should be rewritten to the front-controller as in #1)
  3. Only the parent .htaccess file in the document root can be changed.

We basically need to "override" the 403 Forbidden response for the protected subdirectory when either the directory itself is requested or a non-file in that subdirectory is requested.

Ordinarily, directives in the subdirectory .htaccess file override directives in the parent .htaccess file, so we need to change the order of processing and make sure that the necessary "overriding" directives in the parent .htaccess are processed later. This can be achieved by wrapping the directives in a <Files> (or <FilesMatch>) container or in an <If> block using an Apache expression.

However, an added complication in this particular case is the use of the deprecated (Apache 2.2 and earlier) Order and Deny directives in the protected subdirectory. (On Apache 2.4, which I assume is what you are using, you should be using the single Require all denied directive instead.) In order to "override" the authorisation directives in the subdirectory you must use the same authorisation method. (Which is why the Apache docs stress that you should avoid mixing the two (old and new) authorisation methods. The old/deprecated directives are provided for backwards compatibility only and should be migrated - in their entirety - at the earliest opportunity. Being officially deprecated, they will be removed entirely in future versions of Apache.)

So, you can do something like the following in the root .htaccess file. The order of the directives does not strictly matter, you can place it before or after your mod_rewrite front-controller, however, it would be more logical to have it before.

# For any request that starts "/private" and does not map to a physical file then...
<If "%{REQUEST_URI} =~ m#^/private# && !-f %{REQUEST_FILENAME}">
    DirectoryIndex disabled
    DirectorySlash Off

    # If using Apache 2.4 directives...
    #Require all granted

    # If using old Apache 2.2 directives in the subdirectory then need to use the same here...
    Order Deny,Allow
    Allow from all
</If>

It is necessary to disable the DirectoryIndex for the subdirectory to prevent mod_dir trying to serve the index document (eg. index.php) when requesting the bare directory. If DirectoryIndex is enabled then... if the index document exists then mod_dir issues an internal subrequest which then gets blocked and the URL-path is not accessible to your CMS. If the index document does not exist then mod_dir blocks the request and the URL-path is not accessible to your CMS.

DirectorySlash Off is only required if you want to be able to access /private without a trailing slash. Ordinarily, mod_dir will append the trailing slash with a 301 redirect, since /private is a physical directory.

UPDATE: I've since realised you don't need #2 above, and you want to simply rewrite /private/<anything> to /index.php (your front-controller), even when it would otherwise map to a real file. In which case, your modified suggestion looks good:

<If "%{REQUEST_URI} =~ m#^/private#">
Order Deny,Allow
Allow from all
RewriteRule ^ /index.php [L]
</If>

It repeats the rewrite to the front-controller (which is still present later in the .htaccess file), but this is necessary to override the DirectoryIndex and to force any requests for physical files in the /private subdirectory to also be rewritten to the front-controller (so as to not be accessible).

I'm not sure what =~ m#^/private# does, but I suppose that it check if the url starts with '/private'. I'm also wonder what's the effect if the url starts with /privatesomething

Yes, it checks that the requested URL-path starts /private. =~ is the regex comparison operator and m#<regex># is the alternative syntax for regex. You can also use /<regex>/ (conventional slash delimiters) but that's naturally troublesome when checking URLs.

You can't only check that the URL starts /private/ (with a trailing slash) since a request for /private (no trailing slash) will trigger a 403 response before mod_dir appends the slash.

/privatesomething will also get caught by this but that shouldn't matter since you are only allowing access and rewriting the request - which is what you want to do anyway.

However, if you have physical files in the root that shouldn't be passed to the CMS and served directly, like /privatesomething.jpg, then this will indeed be a problem with the above regex. This can be resolved by tweaking the regex so that it matches either /private or /private/something, but not /privatesomething. For example:

<If "%{REQUEST_URI} =~ m#^/private($|/)#">

A CMS with a public folder and a private folder

Generally, when a CMS is shipped with a /public and /private subdirectory at the same level then the intention, if you have access to the server config, is to change the server's DocumentRoot to point to the /public subdirectory. OR, if you don't have access to the server config, then unconditionally rewrite the request using mod_rewrite to the /public subdirectory. And have a secondary .htaccess file in the /public subdirectory containing your front-controller pattern. (All the necessary files should be shipped with the CMS - nothing to manually edit, except perhaps to comment-out if you have access to the server config.)

This method hides the /public file-path from the visible URL (which is the intention) and naturally resolves the problem discussed above with having a URL conflict with the protected /private subdirectory.

For example, (if not changing the DocumentRoot in the server config), then in the root .htaccess file:

RewriteEngine On

# Unconditionally rewrite all requests to the "/public" subdirectory
RewriteRule ^ /public%{REQUEST_URI} [L]

Then, in /public/.htaccess you have your front-controller:

RewriteEngine On

# CMS front-controller
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

Note the absence of the slash at the start of the RewriteRule substitution string. It should be index.php, not /index.php here.

2
  • 1
    Hi! You're answer is almost close for what I need, because it seems that it overrides the htaccess from the private folder. That's what i really wanted. Anyway, seems that using only the next lines, I won't get a 403 error: <If "%{REQUEST_URI} =~ m#^/private#"> Order Deny,Allow Allow from all RewriteRule ^ /index.php [L] </If>. Now, I'm not sure what =~ m#^/private# does, but I suppose that it check if the url starts with '/private'. I'm also wonder what's the effect if the url starts with /privatesomething. Can you please explain if I'm doing something bad in the above code?
    – Andrei
    Jan 16 at 21:10
  • Ah OK, so you just want to rewrite everything to the front-controller (that does actually make more sense). Your updated code looks good to me. I've updated my answer with this and more explanation and an updated regex to avoid matching a URL of the form /privatesomething, although that shouldn't be a problem in this instance unless /privatesomething mapped to a file that should be served directly.
    – MrWhite
    Jan 17 at 1:59
1

I played around with this a bit, and I don't believe it is possible - at least not in the main .htaccess file without messing with server limits.

I posit it may be possible to override the default behaviour by modifying the apache configuration file.

It might make sense to move your private configuration above your document root (I assume you are using it to include content via reading from PHP or similar, or simply not having it accessible to the web server) - in which case you can create another private subdirectory in your DocumentRoot which is accessible via the webserver.

5
  • "... I don't believe it is possible" - what is not possible exactly?
    – MrWhite
    Jan 16 at 0:17
  • 2
    @MrWhite To modify it so you can make use of . redirect /private/* by only changing the .htaccess file in the root directory.
    – davidgo
    Jan 16 at 0:36
  • A usage case which would match the OP criteria would be to remap example.com/private to redirect to example.com/other-url or to have example.com/private pull its files from a different directory.
    – davidgo
    Jan 16 at 0:39
  • Ahh, penny drops! To be able to route a request for /private/ through the front-controller (/index.php in the root) and presumably /private/<something> (where <something> is not a physical file in the /private subdirectory) should also be routable through the front-controller? But /private/<actual-file> should still be blocked. I believe this is "technically possible", however, it does scream anti-pattern. Moving or renaming the the /private subdirectory, as you suggest, to avoid the conflict in the first place would be the much better solution here.
    – MrWhite
    Jan 16 at 2:43
  • "I don't believe it is possible" - there's a working solution in my answer.
    – MrWhite
    Jan 21 at 18:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.