An internal redirect loop is confirmed when Apache re-processes a rewritten URI and the same RewriteRule matches its own output again, eventually hitting the server recursion limit and returning a 500 error. The loop is not a browser redirect; it is internal processing within the same request.
Confirmed facts vs likely explanation
Confirmed: Apache will restart rewriting after an internal substitution. The [L] flag stops processing for the current pass only, and a subsequent internal redirect starts a new pass. A RewriteCond that does not exclude the transformed URI will cause re-application.
Likely explanation for your case: A rule intended for language routing or proxying matches both the original and the transformed path. Without an exit condition the engine cycles between two states.
Patterns that commonly trigger the error
- Self-referential substitution:
RewriteRule ^/(.*)$ /index.php?url=$1 [L] where the resulting /index.php request still matches ^/(.*)$.
- Broad front-controller without file/directory exclusion: static assets get rewritten to the controller, which then rewrites back.
- Missing termination on proxy chains: a rule rewrites to an internal path that is again captured by an earlier rule.
Refactor steps for this case
- Make the rule non-recursive with an exclusion condition.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
- Use an environment marker when the pattern cannot be excluded by file tests.
RewriteCond %{ENV:REWRITTEN} !1
RewriteRule ^/app/(.*)$ /app/index.php?path=$1 [L,E=REWRITTEN:1]
- Prefer [END] over [L] for final termination when Apache 2.4+ is available and the rule must not be re-entered on internal redirect. [END] stops all further passes.
- Keep complex chains ordered from most specific to most general, and ensure each step changes the URI into a form that will not match the preceding rules.
Verification
Check the active rule set in .htaccess or VirtualHost. Enable rewrite trace logging in the server config to see the URI transformation sequence per request, and confirm the URI toggles between two values.
Assumption: rules are in per-directory context. If they are in server/vhost context, RewriteCond %{REQUEST_FILENAME} behaves differently. To give a precise refactor, one missing diagnostic detail is needed: the exact RewriteRule lines and whether they run in .htaccess or VirtualHost, as that changes the choice between [L] and [END] and the correct test variables.