You're getting a redirect loop because the rewritten URL matches the URL you want to redirect. In .htaccess
the rewrite engine starts-over after the first rewrite, despite the presence of the L
flag. The L
flag simply stops the current pass, it doesn't stop all processing.
- how can I redirect all requests except
api
ones from document root to site
folder without changing the browser uri
- and rewrite browser uri to document root for requests made directly to site folder, i.e. instead of
/site/..
show /..
in
browser uri
You have the terminology reversed. #1 is actually a "rewrite", not a "redirect". You are internally rewriting (URL-rewrite) the request - the visible URL is not changing. And #2 is an external "redirect", not a "rewrite". As in a 3xx external redirect - the browser is being redirected.
I would also reverse the order, so you do #2 - the external redirect - first. Generally, redirects should always go before rewrites.
Try the following instead:
# Redirect direct requests for "/site/<anything>" to "/<anything>"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^site/(.*) /$1 [R=302,L]
# Rewrite "/<anything>" to "/site/<anything>"
# except for "/api" and "/site" itself.
RewriteRule !^(api|site)/ /site%{REQUEST_URI} [L]
The RewriteCond
directive on the redirect that checks against the REDIRECT_STATUS
environment variable prevents a redirect loop. This variable is empty on the initial request and set to "200" (as in 200 OK status) after the first successful rewrite.
The 2nd rule (rewrite) uses a negated pattern to exclude requests to the /api
and /site
directories. This avoids the need for a separate condition. We then need to use the REQUEST_URI
server variable, instead of a backreference, in the substitution string.