Why does Directory work with RewriteRule, but DirectoryMatch fail?

53 Views Asked by At

The DirectoryMatch directive generally works fine for me.

However, when combined with RewriteRule, it can fail. And I don't understand why.

Now, I'm not asking for a workaround which would avoid the DirectoryMatch directive entirely, such as by combining the Directory and Import directives.

My question is specifically about the DirectoryMatch directive. To make it work, what am I missing? What am I not understanding about it? About the rewrite module? Or something else?

By the way, please note that chaining the RewriteRules doesn't enable the DirectoryMatch directive to succeed with this.

No .htaccess files are allowed. (/etc/apache2/apache2.conf contains the AllowOverride None directive.)

I have uploaded the following files. They are already gzipped:

$ ls -dlU `find test-{d,m}*`
drwxr-xr-x 2 root root 4096 Feb  9 06:07 test-directory
-rw-r--r-- 1 root root  439 Feb  8 14:17 test-directory/test.html.gz
drwxr-xr-x 2 root root 4096 Feb  9 06:06 test-match
-rw-r--r-- 1 root root  439 Feb  8 14:18 test-match/test.html.gz

and I have these settings in /etc/apache2/conf-available/local-settings.conf:

RewriteEngine On
<Directory /var/www/html>
   Require all granted
</Directory>

<Directory /var/www/html/test-directory>
   LogLevel debug
   Header set Cache-Control no-store
   RewriteCond "%{REQUEST_FILENAME}.gz" -s
   RewriteRule ^test\.html$ $0.gz
   RewriteRule ^test\.html\.gz$ - [T=text/html,E=no-gzip:1]
</Directory>

<DirectoryMatch "^/var/www/html/test-match">
   LogLevel debug
   Header set Cache-Control no-store
   RewriteCond "%{REQUEST_FILENAME}.gz" -s
   RewriteRule ^test\.html$ $0.gz
   RewriteRule ^test\.html\.gz$ - [T=text/html,E=no-gzip:1]
</DirectoryMatch>

Basically, if the implicit .gz extension when appended to the requested filename exists as a file, then:

Append `.gz`;

Serve the correct media type; and

Prevent mod_deflate from double-gzipping.

Navigating to test.html under Directory works. But under DirectoryMatch it produces "404 Not Found". The error log shows:

$ sudo tail -n 1 /var/log/apache2/error.log
[{...}:07:45.669167 2024] [core:info] [pid 919696:tid 281473097625984] {...} AH00128: File does not exist: /var/www/html/test-match/test.html

So why doesn't the version with DirectoryMatch find the file?

Now in another set of test directories, I have uploaded the following files. They are not gzipped:

$ ls -dlU `find test-simple*`
drwxr-xr-x 2 root root 4096 Feb 12 13:51 test-simple-dir
-rw-r--r-- 1 root root  778 Feb  8 14:17 test-simple-dir/test.html.new
drwxr-xr-x 2 root root 4096 Feb 12 13:51 test-simple-match
-rw-r--r-- 1 root root  778 Feb  8 14:17 test-simple-match/test.html.new

and I have these additional, simpler settings in /etc/apache2/conf-available/local-settings.conf:

<Directory /var/www/html/test-simple-dir>
   LogLevel debug
   Header set Cache-Control no-store
   RewriteCond "%{REQUEST_FILENAME}.new" -s
   RewriteRule ^test\.html$ test.html.new [END]
</Directory>

<DirectoryMatch "^/var/www/html/test-simple-match">
   LogLevel debug
   Header set Cache-Control no-store
   RewriteCond "%{REQUEST_FILENAME}.new" -s
   RewriteRule ^test\.html$ test.html.new [END]
</DirectoryMatch>

Basically, if the .new extension when appended to the requested filename exists as a file, then:

Append `.new`.

Again, navigating to test.html under Directory works. But under DirectoryMatch it produces "404 Not Found". The error log shows:

[{...}:29:30.136369 2024] [deflate:debug] [pid 919696:tid 281473122804096] mod_deflate.c(869): {...} AH01384: Zlib: Compressed 778 to 404 : URL /test-simple-dir/test.html.new
[{...}:29:47.041233 2024] [core:info] [pid 919696:tid 281473262170496] {...} AH00128: File does not exist: /var/www/html/test-simple-match/test.html

Again, why doesn't the version with DirectoryMatch find the file?

Increasing to LogLevel trace6 produced, in the error log:

$ sudo tail  /var/log/apache2/error.log
[{...}:03:10.217345 2024] [core:trace3] [pid 938032:tid 281473262170496] request.c(360): [client {...}:27372] request authorized without authentication by access_checker_ex hook: /test-simple-match/test.html
[{...}:03:10.217356 2024] [rewrite:trace3] [pid 938032:tid 281473262170496] mod_rewrite.c(487): [client {...}:27372] {...} - - [{...}/sid#ffff9b6c4208][rid#ffff984370a0/initial] [perdir ^/var/www/html/test-simple-match/] applying pattern '^test\\.html$' to uri '/var/www/html/test-simple-match/test.html'
[{...}:03:10.217362 2024] [rewrite:trace1] [pid 938032:tid 281473262170496] mod_rewrite.c(487): [client {...}:27372] {...} - - [{...}/sid#ffff9b6c4208][rid#ffff984370a0/initial] [perdir ^/var/www/html/test-simple-match/] pass through /var/www/html/test-simple-match/test.html
[{...}:03:10.217376 2024] [core:info] [pid 938032:tid 281473262170496] [client {...}:27372] AH00128: File does not exist: /var/www/html/test-simple-match/test.html

For diagnostic purposes, in both the simple Directory and DirectoryMatch sections, I temporarily added, immediately after RewriteCond, this RewriteRule:

RewriteRule (.*) https://example.com/?path=$1 [R,L]

Under Directory, the result was a redirect to https://example.com/?path=test.html as desired.

But under DirectoryMatch, it was to https://example.com/?path=/var/www/html/test-simple-match/test.html. In other words, to the absolute file path.

So, under DirectoryMatch, how can I access the relative file path when using the RewriteCond and RewriteRule directives?

Apache version:

$ /usr/sbin/apachectl -v
Server version: Apache/2.4.56 (Debian)
Server built:   2023-04-02T03:06:01

Debian version:

$ cat /etc/debian_version
11.9
0

There are 0 best solutions below