In our previous posts, we explored how Apache 2.4 changed its
handling of directory requests when DirectorySlash Off
is set,
breaking the implicit /dir → /dir/
redirect behavior that worked in
Apache 2.2. We concluded that while an external redirect is the only
reliable fix, this change in behavior led us to an even bigger
question:
Is this a bug or an intentional design change in Apache?
After digging deeper, we’ve uncovered something critically important that is not well-documented:
DirectoryIndex
.This post explores why this happens, whether it’s a feature or a bug, and why Apache’s documentation should explicitly clarify this behavior.
Let’s revisit the problem: we tried using an internal rewrite to append a trailing slash for directory requests:
RewriteEngine On
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteRule ^(.*)$ /$1/ [L]
Expected Behavior:
- Apache should internally rewrite /setup
to /setup/
.
- Since DirectoryIndex index.roc
is set, Apache should serve index.roc
.
Actual Behavior:
- Apache internally rewrites /setup
to /setup/
, but then
immediately fails with a 403 Forbidden.
- The error log states:
AH01276: Cannot serve directory /var/www/vhosts/treasurersbriefcase/htdocs/setup/: No matching DirectoryIndex (none) found
- Apache is treating /setup/
as an empty directory instead of recognizing index.roc
.
Unlike what many admins assume, Apache does not start over after an internal rewrite.
/setup
).mod_rewrite
.index.html
, index.php
, etc.) exists, DirectoryIndex
resolves it.DirectoryIndex
.DirectoryIndex
after an internal rewrite./setup/
as an empty directory with no default file and denies access with 403 Forbidden.First, let’s discuss why this worked in Apache 2.2.
The key reason internal rewrites worked in Apache 2.2 is that Apache restarted the request processing cycle after a rewrite. This meant that:
index.html
, index.php
, or any configured default file.DirectorySlash
was handled earlier in the request cycle,
Apache 2.2 still applied directory handling rules properly, even after
an internal rewrite.In Apache 2.4, this behavior changed. Instead of restarting the
request cycle, Apache continues processing the request from where it
left off. This means that after an internal rewrite, DirectoryIndex
is
never reprocessed, leading to the 403 Forbidden errors we
encountered. This fundamental change explains why no internal solution
works the way it did in Apache 2.2.
mod_rewrite
or DirectoryIndex
docs.Since Apache won’t reprocess DirectoryIndex, the only way to guarantee correct behavior is to force a new request via an external redirect:
RewriteEngine On
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteRule ^(.*)$ http://%{HTTP_HOST}/$1/ [R=301,L]
This forces Apache to start a completely new request cycle,
ensuring that DirectoryIndex
is evaluated properly.
We believe this behavior should be explicitly documented in: - mod_rewrite documentation (stating that rewrites do not restart request processing). - DirectoryIndex documentation (noting that it will not be re-evaluated after an internal rewrite).
This would prevent confusion and help developers troubleshoot these issues more efficiently.
Next post: END
Block Hijacking
Previous post: How to Fix Apache 2.4 Broken Directory Requests (Part II)