Introduction

In our previous blog post, we detailed a clever external redirect solution to address the Apache 2.4 regression that broke automatic directory handling when DirectorySlash Off was set. We teased the question: Can we achieve the same behavior with an internal redirect? Spoiler alert - Nope.

In this follow-up post, we’ll explore why internal rewrites fall short, our failed attempts to make them work, and why the external redirect remains the best and only solution.


The Apache 2.2 vs. 2.4 Behavior Shift

How Apache 2.2 Handled Directory Requests

  • If a user requested /dir without a trailing slash, Apache would automatically redirect them to /dir/.
  • Apache would then serve the directory’s index.html (or any file specified by DirectoryIndex).
  • This behavior was automatic and required no additional configuration.

What Changed in Apache 2.4

  • When DirectorySlash Off is set, Apache stops auto-redirecting directories.
  • Instead of treating /dir as /dir/, Apache tries to serve /dir as a file, which leads to 403 Forbidden errors.
  • DirectoryIndex no longer inherits globally from the document root - each directory must be explicitly configured to serve an index file.
  • Apache does not reprocess DirectoryIndex after an internal rewrite.

The Internal Rewrite Attempt

Our Initial Idea

Since Apache wasn’t redirecting directories automatically anymore, we thought we could internally rewrite requests like this:

 RewriteEngine On

 # If the request is missing a trailing slash and is a directory, rewrite it internally
 RewriteCond %{REQUEST_URI} !/$
 RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
 RewriteRule ^(.*)$ /$1/ [L]

Why This Doesn’t Work:

  • While this internally rewrites the request, Apache does not reprocess DirectoryIndex after the rewrite.
  • If DirectoryIndex is not explicitly defined for each directory, Apache still refuses to serve the file and throws a 403 Forbidden.
  • Unlike Apache 2.2, Apache 2.4 treats /dir as a raw file request instead of checking for an index page.

The Per-Directory Fix (Which Also Fails)

To make it work, we tried to manually configure every directory:

<Directory "/var/www/vhosts/treasurersbriefcase/htdocs/setup/">
    Require all granted
    DirectoryIndex index.roc
</Directory>

Why This Also Fails:

  • Apache does not reprocess DirectoryIndex after an internal rewrite. Even though DirectoryIndex index.roc is explicitly set, Apache never reaches this directive after rewriting /setup to /setup/.
  • Apache still treats /setup/ as an empty directory, leading to a 403 Forbidden error.
  • The only way to make this work per directory would be to use an external redirect to force a new request cycle.

This means that even if we were willing to configure every directory manually, it still wouldn’t work as expected.

FallbackResource (Why This Was a Dead End)

We briefly considered whether FallbackResource could help by redirecting requests for directories to their respective index files:

<Directory "/var/www/vhosts/treasurersbriefcase/htdocs/setup/">
    DirectoryIndex index.roc
    Options -Indexes
    Require all granted
    FallbackResource /setup/index.roc
</Directory>

Why This Makes No Sense

  • FallbackResource is designed to handle 404 Not Found errors, not 403 Forbidden errors.
  • Since Apache already recognizes /setup/ as a valid directory but refuses to serve it, FallbackResource is never triggered.
  • This does not address the fundamental issue that Apache does not reprocess DirectoryIndex after an internal rewrite.

This was a red herring in our troubleshooting FallbackResource was never a viable solution.


Sledge Hammer Approach: Direct Rewrite to the Index File

Another way to handle this issue would be to explicitly rewrite directory requests to their corresponding index file, bypassing Apache’s DirectoryIndex handling entirely:

RewriteEngine On
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteRule ^(.*)$ /$1/index.roc [L]

It works…

  • It completely avoids Apache’s broken DirectoryIndex handling in Apache 2.4.
  • No need for DirectorySlash Off, since the request is rewritten directly to a file.
  • It prevents the 403 Forbidden issue because Apache is no longer serving a bare directory.

…but it’s not ideal

  • Every directory would need an explicit rewrite rule to its corresponding index file.
  • If different directories use different index files (index.html, index.php, etc.), additional rules would be required.
  • This does not scale well without complex conditional logic.

While this approach technically works, it reinforces our main conclusion: - Apache 2.4 no longer restarts the request cycle after a rewrite, so we need to account for it manually. - The external redirect remains the only scalable solution.

Why the External Redirect is the Best Approach

Instead of fighting Apache’s new behavior, we can work with it using an external redirect:

RewriteEngine On
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteRule ^(.*)$ http://%{HTTP_HOST}/$1/ [R=301,L]

Why This Works Perfectly

  • Redirects /dir to /dir/ before Apache even tries to serve it.
  • Preserves DirectoryIndex behavior globally, just like Apache 2.2.
  • No need for per-directory configuration.
  • Ensures SEO-friendly canonical URLs with the correct trailing slash.

Conclusion

So, can we solve with an internal redirect?

  • NO! … because Apache does not reprocess DirectoryIndex after an internal rewrite.
  • Even explicit per-directory DirectoryIndex settings fail if Apache has already decided the request is invalid.
  • FallbackResource never applied, since Apache rejected the request with 403 Forbidden, not 404 Not Found.

Does our external redirect solution still hold up?

Yes!!, and in fact, it’s not just the best solution - it’s the only reliable one.

Lessons Learned

  • Apache 2.4 fundamentally changed how it handles directory requests when DirectorySlash Off is set.
  • Internal rewrites cannot fully restore Apache 2.2 behavior because Apache does not restart request processing after a rewrite.
  • The only way to ensure correct behavior is to use an external redirect before Apache attempts to serve the request.

If you haven’t read our original post yet, check it out here for the full explanation of our external redirect fix.

In part III of this series we’ll beat this dead horse and potentially explain why this was a problem in the first place…


Next post: How to Fix Apache 2.4 Broken Directory Requests (Part III)

Previous post: How to Fix Apache 2.4 Broken Directory Requests