Use FallbackResource even if directory exists

I've set up my virtual host on Apache 2.4.7 with a very basic configuration:

<VirtualHost *:80>
  ServerName foo.example.com
  DocumentRoot /var/www/html

  DirectoryIndex index.php
  FallbackResource /index.php
</VirtualHost>

Underneath the document root I have the following structure:

/index.php
/help/readme.txt

I get the following results when I make requests:

/bla     -> 200 OK
/help/   -> 404 Not Found
/help/a  -> 200 OK

It seems that the existence of the /help/ directory is causing Apache to return 404 because there's no index.php in there, but I expect all requests to invoke /index.php and thus yield a 200 OK response.

I don't remember this being a problem when using mod_rewrite, but I prefer using FallbackResource if possible. Is there a way to fix this?

Update

It works if I remove the DirectoryIndex directive, but that suffers from five second delay issues.

Update 3

I'm running the following test environment; the directory structure is as follows:

./htdocs
   index.html
   test/
      bla.txt
./conf
   httpd.conf
./logs

The contents of httpd.conf is:

ServerName apache-bug.local
Listen 8085

DirectoryIndex disabled
DirectorySlash Off

<VirtualHost *:8085>
DocumentRoot /home/user/apache-bug/htdocs

FallbackResource /index.html
</VirtualHost>

My config.nice contains:

"./configure" \
"--enable-debugger-mode" \
"--with-apr=/usr/local/apr/bin/apr-1-config" \
"--enable-dir=static" \
"--with-mpm=prefork" \
"--enable-unixd=static" \
"--enable-authn-core=static" \
"--enable-authz-core=static" \
"$@"

To run the server:

httpd -X -d /home/user/work/apache-bug/

Solution 1:

I'm answering this myself as well, because I'm pretty sure that the issue is related to how mod_dir.c works internally and I think it's a bug.

If a resource can't be mapped to the local file system, the function fixup_dflt() will run, using the FallbackResource to determine which document should be loaded instead.

However, when a resource can be mapped to the local file system and it's a directory, it will attempt to resolve the document by running fixup_dir(); this function iterates over the list of DirectoryIndex values until it finds the first suitable document.

In my case, the configuration has an empty list of DirectoryIndex values, so fixup_dir() will fail and a 404 is returned.

The following patch works for me (PR):

static int dir_fixups(request_rec *r)
{
    if (r->finfo.filetype == APR_DIR) {
-        return fixup_dir(r);
+        if (fixup_dir(r) != OK) {
+           /* use fallback */
+           return fixup_dflt(r);
+        }
+
+        return OK;
    }
    else if ((r->finfo.filetype == APR_NOFILE) && (r->handler == NULL)) {
        /* No handler and nothing in the filesystem - use fallback */
        return fixup_dflt(r);
    }
    return DECLINED;
}

It basically tries fixup_dflt() after fixup_dir() failed.

Update 2015-04-21

A fix has been submitted to the project, scheduled for 2.5; it may be ported to 2.4 as well.

Update 2015-05-18

The fix has been reverted because:

[...] it [at least] causes FallBackResource to kick in before mod_autoindex might have kicked in.

I'm still trying to figure out how to avoid this kind of situation.

Solution 2:

Your configuration should be correct.

The problem, strangely enough, seems to be mod_deflate.

After having reproduced your configuration successfully here (not getting a 404), I also got the 5 second delay. However, I noticed that when the UA omits gzip from its Accept-Headers, the page is displayed/received instantaneously. You can test this for yourself with wget.

Interestingly, further debugging with strace shows that apache sends the contents of your FallbackResource to the socket of your client without noticeable difference in delay for both cases. This is also evident on the wire, where a reply paket is sent from the server to the client after the HTTP Request without any notable delay1.

It seems that when using mod_deflate in this case however, the UA doesn't know when the data sent by the server ends, and thus does not render anything before the TCP connection times out2 and is forcibly closed by the server. This is compliant to HTTP/1.0, where a closed connection signifies end-of-content.

For HTTP/1.1, the server has other means available to signal the end-of-content – but none of which seem to happen here.

Wether the bug lurks in mod_dir or mod_deflate however, is beyond my available time right now. I got it to work flawlessy by disabling gzip compression; as a workaround until the issue is fixed for good, you can selectively disable gzip.

1) This tells us that the problem does not stem from unflushed buffers on the server.
2) By default, the timeout is 5 seconds with apache - that's where your 5 seconds are from.