1

In my Apache setup, I have a directory with my error pages in it (404 403). However, I don't want this directory being viewable directly - I'd like accessing it to return a 404. So what I did was this:

# Use /hidden/404/ as the 404 page
ErrorDocument 404 /hidden/404/
# Use /hidden/403/ as the 403 page
ErrorDocument 403 /hidden/403/
<Directory /path/to/root/hidden/>
    # All requests return 404
    RedirectMatch 404 .*
</Directory>

the problem is that Apache seems to be unwilling to serve the files at all in this case, even when I tell it to explicitly (that is, to handle 404 and 403 errors). In fact, when I visit /hidden, I get the following message:

The requested URL /hidden/404/ was not found on this server.

Additionally, a 404 Not Found error was encountered while trying to use an 
ErrorDocument to handle the request.

Any way I can tell Apache to disallow access to the directory while still allowing itself to serve those files when necessary?

joshlf
  • 119
  • 6

1 Answers1

0

OK I figured it out a way to do it.

What I did was use mod_rewrite, which is an Apache module (shipped by default with all installations) for rewriting requested URLs on the fly. Here's the code:

# Custom error pages
ErrorDocument 404 /hidden/404/index.html
ErrorDocument 403 /hidden/403/index.html

# Pretend that /hidden doesn't exist
# (unless this is an internal redirect,
# such as rendering a 404 or 403)
RewriteCond %{ENV:REDIRECT_STATUS} =""
RewriteRule ^/hidden/.* - [L,R=404]

So let's walk through these lines. The ErrorDocument directives haven't changed - they just say what page should be served when a given error occurs.

The real meat is in the RewriteCond and RewriteRule directives. What the RewriteRule directive says is this: if a url is requested whose path matches the regex ^/hidden/.* (essentially, any path that is to anything in /hidden/ or its subtree), leave the url unchanged (that's what the - means), but don't interpret any more RewriteRule directives (that's what the L means), and return a 404 error code (that's what the R=404 means).

This, combined with the ErrorDocument 404 /hidden/404/index.html directive, might seem to do the trick. The problem, however, is that when Apache goes to serve /hidden/404/index.html, it runs through these rules again. That means that the RewriteRule directive is executed, which in turn says to render the 404, and the whole thing just infinite-loops.

Thus, we use the RewriteCond directive to make sure this doesn't happen. RewriteCond directives are conditionals - the RewriteRule directive below will only be executed if the condition is satisfied. The condition in this case is "the environment variable REDIRECT_STATUS is equal to the empty string." Now, REDIRECT_STATUS is an environment variable that is set by ErrorDocument, and holds the status code that is being redirected (so, if the ErrorDocument 404 ... directive had been triggered, it would be set to the value "404"). However, if no ErrorDocument directive had been executed, then REDIRECT_STATUS would not have been set, and so asking for its value would simply return the empty string. Thus, RewriteCond %{ENV:REDIRECT_STATUS} ="" essentially says, "don't bother evaluating the RewriteRule directive below unless we're not in the middle of serving an error page."


note: if you have looked at the RewriteRule documentation, you may have noticed that the R argument is supposed to perform an explicit redirect. The reason it works fine in this case is that there's an undocumented feature of the R argument which is that it only redirects on 300-class codes. Thus, R=404 does not do an explicit redirect, only an internal one.

joshlf
  • 119
  • 6