Introduction

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:

Apache does not restart the request cycle after an internal rewrite, and this can break expected behaviors like 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.


What Happens When Apache Internally Rewrites a Request?

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.


The Key Issue: Apache Does Not Restart Request Processing After an Internal Rewrite

Unlike what many admins assume, Apache does not start over after an internal rewrite.

How Apache Processes Requests (Simplified)

  1. The request arrives (/setup).
  2. Apache processes mod_rewrite.
  3. Apache determines how to serve the request.
  4. If an index file (index.html, index.php, etc.) exists, DirectoryIndex resolves it.

Why Internal Rewrites Don’t Restart Processing

  • Apache processes mod_rewrite before it checks DirectoryIndex.
  • Once a rewrite occurs, Apache continues processing from where it left off.
  • This means it does not re-check DirectoryIndex after an internal rewrite.
  • Instead, it sees /setup/ as an empty directory with no default file and denies access with 403 Forbidden.

Is This a Bug or a Feature?

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:

  • After an internal rewrite, Apache treated the rewritten request as a brand-new request.
  • As a result, it re-evaluated DirectoryIndex and correctly served index.html, index.php, or any configured default file.
  • Since 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.

Why It’s Likely an Intentional Feature

  • In Apache 2.2, some rewrites did restart the request cycle, which was seen as inefficient.
  • In Apache 2.4, request processing was optimized for performance, meaning it does not restart after an internal rewrite.
  • The behavior is consistent across different Apache 2.4 installations.
  • Some discussions in Apache’s mailing lists and bug tracker mention this as “expected behavior.”

Why This is Still a Problem

  • This behavior is not explicitly documented in mod_rewrite or DirectoryIndex docs.
  • Most admins expect Apache to reprocess the request fully after a rewrite.
  • The lack of clarity leads to confusion and wasted debugging time.

Implications for Apache 2.4 Users

1. Mod_Rewrite Behavior is Different From What Many Assume

  • Internal rewrites do not restart request processing.
  • DirectoryIndex is only evaluated once, before the rewrite happens.
  • This is not obvious from Apache’s documentation.

2. The Only Reliable Fix is an External Redirect

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.

3. Apache Should Improve Its Documentation

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.


Conclusion: A Feature, But a Poorly Documented One

  • The fact that Apache does not restart processing after an internal rewrite is likely an intentional design choice.
  • However, this is not well-documented, leading to confusion.
  • The only solution remains an external redirect to force a fresh request cycle.
  • We believe Apache should update its documentation to reflect this behavior more clearly.

Next post: END Block Hijacking

Previous post: How to Fix Apache 2.4 Broken Directory Requests (Part II)