3

I have a problem that it seems should be simple. I want to apply a group of configuration directives to a single file (in this case myfile.html in the DocumentRoot). There may also be subdirectories containing myfile.html, these should not get the configuration. I also want to do it without using Location or LocationMatch.

I came up with the following

DocumentRoot "/home/amoe/opt/httpd/htdocs"
<DirectoryMatch "^/home/amoe/opt/httpd/htdocs$">
    <Files "myfile.html">
        ExpiresActive on
        ExpiresDefault A10
    </Files>
</DirectoryMatch>

However, the Expires header doesn't get set when requesting /myfile.html. Adding a trailing slash on the DirectoryMatch seems to make no difference.

You can assume that I want to do this without Location for academic reasons. (Edit: Also, to quote the documentation, It is important to never use when trying to restrict access to objects in the filesystem.)

amoe
  • 185
  • 1
  • 9
  • Have you tried using .htaccess files in each directory you need to modify the settings? – Gmck Oct 24 '16 at 16:12
  • @Gmck I also don't want to use `.htaccess` -- again it's a personal preference but I find it makes things difficult to manage. – amoe Oct 24 '16 at 16:28
  • 1
    Are you opposed to using a configuration management system like Salt or Chef? This type of tool can make file management and placement in this sort of scenario easier. – Matt Oct 27 '16 at 13:17
  • @Matt I already use configuration management but it seems to me that `.htaccess` should not be required for this case. – amoe Oct 27 '16 at 19:39

1 Answers1

4

The only way I have found to make this work is by using the following:

<DirectoryMatch ^/home/amoe/opt/httpd/htdocs/[^/]+$>
    <Files "test.html">
        ExpiresActive on
        ExpiresDefault A10
    </Files>
</DirectoryMatch>

I believe that the extra [^/]+ shouldn't be required to get this to work, see below for the full explanation of how I came to this conclusion, and as such I believe this is a bug in Apache.


All of the following is based on an Ubuntu 16.04.1 LTS environment running Apache 2.4.23 with test files in these locations:

  • /path/test/test.html
  • /path/test/noheader-test.html
  • /path/test/child/test.html
  • /path/test/child/grandchild/test.html

Firstly I tried a number of different regex variants, all of which failed to match:

<DirectoryMatch ^/path/test$>
<DirectoryMatch ^/path/test/$>
<DirectoryMatch /path/test/$>
<Directory ~ ^/path/test$>
<Directory ~ ^/path/test/$>
<Directory ~ /path/test/$>

I found however that if you omit the EOL anchor ($) then it matches:

<DirectoryMatch ^/path/test/>
    <Files "test.html">
        ExpiresActive on
        ExpiresDefault A10
    </Files>
</Directory>

However this ofcourse matches the test.html files in all the subdirectories:

  • /path/test/test.html - Expires header sent
  • /path/test/noheader-test.html - no Expires header sent
  • /path/test/child/test.html - Expires header sent
  • /path/test/child/grandchild/test.html - Expires header sent

It was as though there was something else in the path that the regex wasn't matching because when I changed the regex to ^/your/path/test/.+$ it matched.

Wanting to know what this missing part of the path was I used a regex capture group and the Header directive to get Apache to tell me what was missing, like so:

<DirectoryMatch ^/path/test/(?<MISSINGPART>.+)$>
    <Files "test.html">
        Header add X-TEST "expr=%{env:MATCH_MISSINGPART}"
    </Files>
</DirectoryMatch>

This gave the following headers:

  • /path/test/test.html - x-test:"test.html"
  • /path/test/noheader-test.html - no header sent
  • /path/test/child/test.html - x-test:"child/test.html"
  • /path/test/child/grandchild/test.html - x-test:"child/grandchild/test.html"

This suggests that DirectoryMatch is matching the file name too?!

On the Apache bug tracker bug 41867 somewhat fits this description so may explain this (I also tested this without the nested <Files> directive and the header values remained the same)

So to prove it I tried substituting test.html into the regex like so:

<DirectoryMatch "^/path/test/test.html$">
    <Files "test.html">
        Header add X-TEST "Match"
    </Files>
</DirectoryMatch>

No X-TEST header was returned.

This did however mean that the regex could be made to match anything that isn't a directory after the directory we're interested in matching, like so:

<DirectoryMatch "^/path/test/[^/]+$">
    <Files "test.html">
        ExpiresActive on
        ExpiresDefault A10
    </Files>
</DirectoryMatch>

This gives the header in all the desired places:

  • /path/test/test.html - Expires header sent
  • /path/test/noheader-test.html - no Expires header sent
  • /path/test/child/test.html - no Expires header sent
  • /path/test/child/grandchild/test.html - no Expires header sent
MoeBrowne
  • 346
  • 1
  • 5