mod_rewrite loops even with L flag - apache

I've got a problem with rewriting a URL to a fastcgi dispatcher. If I leave only:
RewriteRule ^(.*)$ dispatch.fcgi/$1 [L,QSA]
I expected L (last rule) to cause only a single rewrite. Instead, it keeps prepending dispatch.fcgi until apache reports an error.
I know it can be fixed with:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ dispatch.fcgi/$1 [L,QSA]
But what is the reason for multiple rewrites? Does L do something else than I think it does?

I know it's an old question, but to others searching for the REAL answer, here it is:
The [L] flag DOES work in .htaccess files. It tells the rewrite module to skip all of the following rules in that particular .htaccess file. It does its job, Apache rewrites the url and exits the .htaccess file.
However, at the end of the .htaccess file if the request url has been rewritten, the whole url matching process starts again with the new url.
This is what happens above, ^(.*)$ will always match the current url, it causes an infinite loop, only the maxredirect rewrite option (10 by default) stops it.
The !-f file attribute test (as mentioned by the questioner) would solve the problem, since the url will match a real filename:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ dispatch.fcgi/$1 [L,QSA]
now, if we request http://example.com/toappend, .htaccess rewrites it to dispatch.fcgi/toappend and no rewrite loop will happen.

Hy , add this after RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule .* - [L]
.. and it should work stoping loops .

Apparently -- and I only read this here, I have no first hand knowledge -- the [L] directive does not work in .htaccess files, only if its in your .conf file.
See: Hidden features of mod_rewrite
within the .htaccess context, [L] will
not force mod_rewrite to stop. it will
continue to trigger internal

Faced the same problem, and it turns out that the best solution in Apache 2.3.9+ is to use END flag instead of L as it prevents mod_rewrite from looping over rules.

Related

RewriteRule unexpectedly return 301

I'm lost, I spent hours into this simple issue and can't figure what I'm doing wrong here.
This works as expected:
RewriteEngine on
RewriteRule ^([A-Za-z0-9-_.]+)/([A-Za-z0-9-_.]+/)$ index.php?eins=$1&%{QUERY_STRING}&zwei=$2&%{QUERY_STRING} [L]
and rewrites this request: https://domain.tld/asdf/asdf/ internally
to https://domain.tld/index.php?eins=asdf&zwei=asdf
so far so good everything as expected.
But if I add this additional 2nd rule
RewriteEngine on
RewriteRule ^([A-Za-z0-9-_.]+)/([A-Za-z0-9-_.]+/)$ index.php?eins=$1&%{QUERY_STRING}&zwei=$2&%{QUERY_STRING} [L]
RewriteRule ^(.+[^/])$ /$1/ [R=301,L]
Apache responds to the same request https://domain.tld/asdf/asdf/ with 301 to redirect to https://domain.tld/index.php/?eins=asdf&&zwei=asdghi/
I expected The first RewriteRule should be the L = Last one, but why is it redirecting? Actually I would like to achieve a 301 redirect only if a trailing slash is missing.
Check the RewriteRule Flags L|last,
If you are using RewriteRule in either .htaccess files or in
sections, it is important to have some understanding of
how the rules are processed. The simplified form of this is that once
the rules have been processed, the rewritten request is handed back to
the URL parsing engine to do what it may with it. It is possible that
as the rewritten request is handled, the .htaccess file or
section may be encountered again, and thus the ruleset may be run
again from the start. Most commonly this will happen if one of the
rules causes a redirect - either internal or external - causing the
request process to start over.
If you just want to add the trailing slash only for directory, you could try these rules:
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} !(.+)/$
RewriteRule ^(.*) $1/ [L,R=301]

How to add "everything else" rule to mod_rewrite

How can I make mod_rewrite redirect to a certain page or probably just throw 404 if no other rules have been satisfied? Here's what I have in my .htaccess file:
RewriteEngine on
RewriteRule ^\. / [F,QSA,L]
RewriteRule ^3rdparty(/.*)$ / [F,QSA,L]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^((images|upload)/.+|style.css)$ $1 [L]
RewriteRule ^$ special [QSA]
RewriteRule ^(special|ready|building|feedback)/?$ $1.php [QSA,L]
RewriteRule ^(ready|building)/(\d+)/?$ show_property.php?type=$1&property_id=$2 [QSA,L]
RewriteRule . error.php?code=404 [QSA,L]
This is supposed, among other things, to send user to error.php if he tries to access anything that was not explicitly specified here (by the way, what is the proper way to throw 404?). However, instead it sends user from every page to error.php. If I remove the last rule, everything else works.
What am I doing wrong?
What is happening is that when you are doing a rewrite, you then send the user to the new URL, where these rewrite rules are then evaluated again. Eventually no other redirectoin rules will be triggered and it will get to the final rule and always redirect to the error.php page.
So you need to put some rewrite conditions in place to make this not happen.
The rewrite engine loops, so you need to pasthrough successful rewrites before finally rewriting to error.php. Maybe something like:
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_URI} !^/(special|ready|building|feedback|show_property)\.php
RewriteCond %{REQUEST_URI} !^/((images|upload)/.+|style.css)$
RewriteRule ^ error.php?code=404 [QSA,L,R=404]
Each condition makes sure the URI isn't one of the ones your other rules have rewritten to.
The R=404 will redirect to the error.php page as a "404 Not Found".
Unfortunatelly, it didn't work - it allows access to all files on the server (presumably because all conditions need to be satisfied). I tried an alternate solution:
Something else must be slipping through, eventhough when I tested your rules plus these at the end in a blank htaccess file, it seems to work. Something else you can try which is a little less nice but since you don't actually redirect the browser anywhere, it would be hidden from clients.
You have a QSA flag at the end of all your rules, you could add a unique param to the query string after you've applied a rule, then just check against that. Example:
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^((images|upload)/.+|style.css)$ $1?_ok [L,QSA]
then at the end:
RewriteCond %{QUERY_STRING} !_ok
RewriteRule ^ error.php?code=404&_ok [QSA,L,R=404]
In theory if none of the rules are matched (and the requested URL does not exist), it's already a 404. So I think the simplest solution is to use an ErrorDocument, then rewrite it:
RewriteEngine On
ErrorDocument 404 /404.php
RewriteRule ^404.php$ error.php?code=404 [L]
# All your other rules here...
You can do the same for any other HTTP error code.
The problem here is that after the mod_rewrite finishes rewriting the URL, it is resubmitted to the mod_rewrite for another pass. So, the [L] flag only makes the rule last for the current pass. As much better explained in this question, mod_rewrite starting from Apache version 2.3.9, now supports another flag - [END], that makes the current mod_rewrite pass the last one. For Apache 2.2 a number of solutions are offered, but since one of them was a bit clumsy and another didn't work, my current solution is to add another two rules that allow a specific set of files to be accessed while sending 404 for everything else:
RewriteRule ^((images|upload)/.+|style.css|(special|ready|building|feedback|property).php)$ - [QSA,L]
RewriteRule .* - [QSA,L,R=404]
I think your last rule should be
RewriteRule ^(.*)$ error.php?code=404&query=$1 [QSA,L]
You could leave out the parenthesis and the $1 parameter, but maybe it's useful to know, what the user tried to achieve.
Hope, this does the trick!

Mod_rewrite rules from root

This problem has been bugging me for a while now.
I have a created a small site engine and I'm using mod_rewrite to tell the engine what page to proccess, SEO friendly links is a bonus :).
This is how it's works today:
the adress http://www.example.com/site/page
becomes http://www.example.com/engine.php?address=page
But what i want is:
the adress http://www.example.com/page
becomes http://www.example.com/engine.php?address=page
Everything works fine if i create a psuedo directory for the calls (/site) but when i try to do the same from the root strange things start to happends.
RewriteBase /
RewriteRule ^site/(.*) engine.php?%{QUERY_STRING}&address=$1
Works fine: /site/about/contacts becomes eninge.php?address=about/contacts
RewriteBase /
RewriteRule ^(.*)$ eninge.php?%{QUERY_STRING}&address=$1
Doesn't work, for some reason /about/contacts becomes eninge.php?address=eninge.php
(.*) means catch anything. Have you tried exluding files and directory before your catch-all ? Because it will cause an infinite recursion without it.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ eninge.php?%{QUERY_STRING}&address=$1 [L]
More information is available in the official documentation: http://httpd.apache.org/docs/current/mod/mod_rewrite.html
Update: You should also specify [L] at the end of your rule, to tell Apache to end the rewriting process here.
Check the RewriteLog (this has been updated in 2.4, check current docs if not using 2.2):
RewriteLog "/usr/local/var/apache/logs/rewrite.log"
RewriteLogLevel 3
This will show you exactly what mod_rewrite is doing and allow you to tune your configuration based on its output. Beware - it grows very quickly, and should never be used in production environments.
As an aside, you have some typos in your post - worth verifying that these differ from your config.
RewriteCond %{REQUEST_FILENAME} !^engine.php
RewriteRule (.*) engine.php?address=$1 [QSA,L]
Try this. What you have is causing the rewrite to loop around and first do engine.php?address=about/contacts as you were expecting, but then go around again and rewrite that to engine.php?address=engine.php. Make sense? The [QSA,L] is a Query String Append and Last flag that will add the query string to your URL and tell the rewrite engine to stop looking for rewrites. The RewriteCond %{REQUEST_FILENAME} !^engine.php is to check that you haven't already specified the engine rewrite by ensuring the current URL doesn't start with engine.php. This is necessary if you are writing this in an .htaccess file rather than the .httpd config files.

CodeIgniter on subdomain and htaccess

I'm trying to setup a website based on CodeIgniter.
For developing I want to have the domain dev.XXX.com
I changed my structure so I've got a no_www and a public_www folder.
Both are in the root of dev.XXX.com
So my idea was to rewrite url's form
dev.XXX.com/index.php/test
to
dev.XXX.com/public_www/index.php/test
So I just want add public_www to all the requests
In my htaccess file I've got:
RewriteRule ^(.*)$ /public_www/$1 [L]
But I always get 500 - Internal Server Error
Try adding the following to the .htaccess file in the root directory (public_www) of your site.
RewriteEngine on
RewriteBase /
#if its on dev.xxx.com
RewriteCond %{HTTP_HOST} ^dev\.XXX\.com$ [NC]
#if its not already public_www/ rewrite to public_www/
RewriteRule ^(?!public_www/)(.*)$ /public_www/$1 [L,NC]
The rule you had would lead to an infinite rewrite and subsequent 500 error. The one above should prevent that.
EDIT
could you maybe explain why my version leads into a infinite loop
I am likely incorrect about the infinite loop, but below is what will happen
Start with any input
^(.*)$ pattern will match any input
It will rewrite to /public_www/$1
.htaccess rules will be run again
^(.*)$ pattern will match rewritten input /public_www/$1
It will rewrite to /public_www/public_www/$1
At this point it will likely fail as the directory does not exist...
Your RewriteRule pattern ^(.*)$ will match all input and will rewrite to . The .htaccess rules will then be run again

Apache mod_rewrite issue

I am trying to send every request to www.example.com/user/ to www.example.com/user.php?id=0 using this
RewriteRule ^user/$ user.php?id=0
Basically, if someone is accessing www.example.com/user/ with no user id, the site will default to id = 0.
However, when I type www.example.com/user/ Apache seems to simply serve the user.php file, completely ignoring the RewriteRule. Any idea on why this is happening?
Thank you.
I should mention that this only happens if I use the same word in the URL as the php file's name. For example, if I were to use
RewriteRule ^yes/$ user.php?id=0
Going to www.example.com/yes/ would apply the RewriteRule just fine.
So it seems that Apache looks for a file with that name and ignores the RewriteRule.
And no, adding a [L] flag did not help.
Here's my .htaccess:
RewriteEngine On
RewriteRule ^user/$ user.php?id=0
RewriteRule ^user/([0-9]+)$ user.php?id=$1
try this:
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^user/$ user.php?id=0 [L,NC,QSA]
RewriteRule ^user/([0-9]+)/?$ user.php?id=$1 [L,NC,QSA]
The [L] flag causes mod_rewrite to stop processing the rule set. In most contexts, this means that if the rule matches, no further rules will be processed. This corresponds to the last command in Perl, or the break command in C. Use this flag to indicate that the current rule should be applied immediately without considering further rules.
from: http://httpd.apache.org/docs/current/rewrite/flags.html#flag_l
I think your rewrite rules are in the wrong order, and you're not using the [L] flag to tell apache not to run any more rules when a rule's been matched. Also you could use the + operator instead of * to match at least one digit in your second rule:
RewriteRule ^user/$ user.php?id=0 [L]
RewriteRule ^user/([0-9]+)$ user.php?id=$1 [L]