1

In the document root, I put an .htaccess with:

ErrorDocument 404 /error.php

Since this is in a .htaccess file, Apache looks for error.php in the relative path, so I can put different error.php files in different subfolders to get it to execute different error.php:

request /app1/not-exists.txt : yields /app1/error.php
request /app2/not-exists.txt : yields /app2/error.php
request /not-exists/not-exists.txt : yields /error.php

This is desired behavior. However,

request /not-exists/app1/not-exists.txt : yields /app1/error.php
request /app2/app1/not-exists.txt : yields /app1/error.php

This does not seem like expected behavior. I expected:

request /not-exists/app1/not-exists.txt : yields /error.php
request /app2/app1/not-exists.txt : yields /app2/error.php (or maybe /error.php)

or at worst, some generic Apache error handling. Am I misunderstanding what Apache is supposed to do here? The documentation doesn't seem to make this clear.

dlo
  • 451
  • 1
  • 4
  • 14

1 Answers1

1

I think your misunderstanding here is the relative pathing.

.htaccess files don't have any special behavior that causes paths to be relative; they're essentially the same thing as a <Directory> block in terms of configuration behavior.

ErrorDocument doesn't have any concept of context; when you enter a path like /error.php that's always assumed to be relative to the document root, regardless of where it's configured. mod_rewrite configuration in a <Directory> block or .htaccess file uses relative paths, which is probably what you're thinking of for that behavior.

A couple options for how you could implement this.. you could have a single error.php that pulls in content from the per-app error files depending on the request path?

Or you could just use mod_rewrite to get the desired error page picking behavior (getting the logic to match what you're looking for is a bit of a complicated mess, though):

<Directory /path/to/docroot>
    # First, we'll have a rule that will handle looking for an error.php in
    # the app directory that the request is in...
    # Not a file or a directory that exists:
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    # Capture the application directory that we're trying to load from (if
    # there is one) for use in the next rule:
    RewriteCond %{REQUEST_FILENAME} ^/path/to/docroot/([^/]+)/
    # Check if an error.php exists in that directory;
    RewriteCond /path/to/docroot/%1/error.php -f
    # Send the response to the error page.  It should set the 404 response code.
    RewriteRule ^([^/]+)/.*$ $1/error.php [L]

    # Ok, the previous pile of rules didn't apply;  if the user's requesting 
    # a nonexistent resource, then either the user's not in an app subdirectory,
    # or they're in a subdirectory that didn't have an error.php.
    # So, this is a lot simpler:
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^ error.php [L]
</Directory>
Shane Madden
  • 112,982
  • 12
  • 174
  • 248
  • Regarding your line upfront "ErrorDocument doesn't have any concept of context; when you enter a path like /error.php that's always assumed to be relative to the document root" ... why does my first example (/app1/notexists.txt) yield /app1/error.php instead of /error.php? – dlo Sep 19 '12 at 19:23
  • @dlo Are you certain that's the behavior that you're seeing? If it is, I suspect that there's something mapping those directories together in some way; you'd need to provide your full config for me to tell you how that's happening. – Shane Madden Sep 19 '12 at 21:01
  • Sorry for the delay--I'd like to close this out and accept your answer, but I have one important question: what settings might affect Apache's "loose" behavior? eg- I just noticed that if I have a page called Page1.php, and if I request Page1 without the extension, the page still gets served. Perhaps that same behavior is causing the directory matching behavior as well, but looking through apache's conf, I don't see anything that would create this behavior--any pointers? – dlo Oct 09 '12 at 14:21
  • And btw, it appears DefaultType is text/plain (and there's no ForceType setting) – dlo Oct 09 '12 at 14:23
  • Looks like it's probably Multiviews doing the loose mapping... I'll test this out a little later on. – dlo Oct 09 '12 at 14:35
  • Yup, it should be MultiViews doing that. It ought to just be handling files when no exact match to the request is found, so it likely is not involved in the directory oddness. – Shane Madden Oct 09 '12 at 15:34
  • Just tested, and indeed, the extensionless behavior changed by disabling MultiViews, but you're right, the directory behavior in the original question did not. So I still don't understand why /not-exists/app1/not-exists.txt yields /app1/error.php. While your answer may be a fine fix, could you explain the behavior I'm seeing? – dlo Oct 09 '12 at 18:33