mod_rewrite seems to ignore [L] flag - apache

I'm trying to use the [L] flag in RewriteRule, but it doesn't seem to work. I'd like that if you call the page:
www.domain.com/admin/
it redirects you to:
www.domain.com/backend.php
Otherwise, if you call any other page (except for some pages) it redirects to:
www.domain.com/index.php
Here is what I have so far:
RewriteEngine on
RewriteRule ^admin/(.*) /backend.php/$1 [L]
RewriteCond $1 !^(index\.php|admin|assets|images|uploads|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]
If I use only the first rule, it works (but obviously doesn't redirect other pages to index.php). If I add the second rule, the server seems to ignore the first rule (I think it is "overwritten" by the second rule, even if I added the [L] flag)

This isn't how L works. The rewrite engine will continually loop through all the rules until the URI going into the engine is the same as the one coming out. The L just tells the rewrite engine to stop applying rules in the current loop iteration. So say you have:
RewriteRule ^(.*)$ /foo/$1 [L]
RewriteRule ^(.*)$ /bar/$1 [L]
after 1 iteration, given the URI /blah, I get /foo/blah because it stops rewriting after the first rule (but will still continue to loop). If I remove the L:
RewriteRule ^(.*)$ /foo/$1
RewriteRule ^(.*)$ /bar/$1
after 1 iteration, given the URI /blah, I get /bar/foo/blah. Both rules get applied, one after the other because the L isn't there to stop it.
You need to add a condition in your second rule to prevent it from rewriting the first, either one of these will do:
RewriteCond $1 !^(backend\.php|index\.php|admin|assets|images|uploads|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]
or:
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond $1 !^(index\.php|admin|assets|images|uploads|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]

Your second rule is strange. I do not know what you are attempting to do by putting $1 as the value you are checking your condition against. It should probably look like this:
RewriteEngine on
RewriteRule ^admin/(.*) /backend.php/$1 [L]
RewriteCond %{REQUEST_FILENAME} !^(index\.php|admin|assets|images|uploads|robots\.txt|backend\.php)
RewriteRule ^(.*)$ /index.php/$1 [L]
Note I have also added a pass-through for backend.php

Related

How do I Redirect and Show Content with same urls?

I have a maybe simple Problem. I have serveral URLs that needed to be redirected in this way:
if the URL "/abc/" is called, it should show the content located under "xyz.html"
i can do that with
RewriteRule abc$ xyz.html
but "xyz.html" should be 301 redirecting to "/abc" if it is called.
This is my simple problem i am searching for an solution since hours.
it would be easy if its like "test.html" and /test/. i can do it like
RewriteRule ^(.+)\.html$ /$1 [L,R=301]
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule !.*\.html$ %{REQUEST_URI}.html [END]
But in my case i have a bunch of URLs with no pattern.
All i tried results to server misconfiguration.
Can you help me with that one sample?
thanks, kanuddel
Could you please try following.
RewriteEngine ON
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/abc/? [NC]
RewriteRule ^(.*) /xyz.html [L]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/xyz\.html/? [NC]
RewriteRule ^(.*) /abc/? [R=301,L]
Detailed Explanation:
Why there is redirection loop with normal rule writing: Question from OP is interesting and contradictory(kind of), why because lets say we write 1st rule to redirect(in backend) from abc url to xyz.html it will work fine. But then when we write our 2nd rule which is to rewrite url from xyz.html to abc/ which is exactly opposite of first rule. Hence it becomes a loop, ASAP 1st rules gets served it reaches out to 2nd rule and it serves it back to 1st rule thus a REDIRECTION INFINITE LOOP(till 50 cycles or so).
How to prevent redirection loop?
First since both rules are exactly opposite of each other so in a normal rule writing it will become a loop, now how could we prevent it to become a loop? Answer is using: RewriteCond(explained in next step)
I have used an ENVIRONMENT VARIABLE named ENV:REDIRECT_STATUS which will have any redirection status in its value. 2 things we need to understand about this variable.
1- Without any request redirection its default value is NULL.
2- Whenever we do a successful redirection its value becomes 200(NON ZERO), which we can make use of in our conditions part.
Detailed explanation of htaccess Rules:
Now coming to the condition explanation part: In RewriteRuleRewriteRule ^(.*) /xyz.html [L] I have NOT done any rewriting of url on browser why because of the THUMB RULE that we always want to show USER FRIENDLY URLs to users, so environment variable ENV:REDIRECT_STATUS will always be ZERO here.
Coming to 2nd RewriteRule now RewriteRule ^(.*) /abc/? [R=301,L] where user already using a user NON-friendly URL so first thing is I need to rewrite URL in browser to user friendly url hence R=301(redirection with permanent flag is used here). Once Redirection happens through this condition, 1st condition will start failing now why because that checks if REDIRECT_STATUS variable is NULL which is NOT after serving redirection from 2nd condition. Hence this is how it prevents loop by this small trick :)
Thanks for the great Explanation!
I tried it with a second URL, where "/xxx/" should show "zzz.html" But this gave me an Misonfiguration. I tried to shorten it like this:
RewriteEngine ON
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/abc/? [NC]
RewriteRule ^(.*) /xyz.html [L]
RewriteCond %{REQUEST_URI} ^/xxx/? [NC]
RewriteRule ^(.*) /zzz.html [L]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/xyz\.html/? [NC]
RewriteRule ^(.*) /abc/? [R=301,L]
RewriteCond %{REQUEST_URI} ^/zzz\.html/? [NC]
RewriteRule ^(.*) /xxx/? [R=301,L]

Wrong GET variable after Mod Rewrite

I have a problem with Mod Rewrite.
RewriteRule ^([^-]*)/$ $1.php
RewriteRule ^page/([^-]*)/$ /showpage.php?id=$1 [L]
So when I type example.com/page/ it goes to page.php and this is ok
but when I want go to page/1/ (example.com/showpage.php?id=1) and try to get
$_GET['id'] variable it gives me back this
1.php/1
What's happening:
/page/1/ : (first rule matches) rewrite to/page/1.php
/page/1.php : (second rule matches) rewrite to /showpage.php?id=1.php
To avoid it:
you have to add a [L] flag after your first rule too.
you can use a better pattern. For instance [^/]+ which
means at least one character which is not a slash (will prevent
rules conflict in your case).
you can check if files exist (not mandatory, but better).
This should be working as expected
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}/$1\.php -f
RewriteRule ^([^/]+)/$ /$1.php [L]
RewriteRule ^page/([^/]+)/$ /index.php?id=$1 [L]

why does a matched rewriterule not work?

I have the following .htaccess:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^a/(.*)$ api.php?params=$1 [L,QSA]
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
</IfModule>
I expect that when I go to /a/test that the server returns /api.php?params=test
Instead, the third rule is matched.
If I comment out the third rule, then the first rule works.
Why is that?
Eventhough you have the L flag, which stops rewriting for the current rewriting iteration, the result (the rewritten URI) will be put back into the rewrite engine, and will continue to do so, until the URI going into the rewrite engine comes out unchanged. So what's happening is the first rule gets applied, then api.php?params=test is put back into the rewrite engine, where the 3rd rule gets applied.
You can either turn off all looping, by passing through the URI if an internal redirect was made, by adding this right underneath RewriteEngine On:
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
Or add a condition to the 3rd rule so that it ignores requests to existing resources:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) app/webroot/$1 [L]
Or add an explicit condition to ignore api.php:
RewriteCond %{REQUEST_URI} !^/api\.php
RewriteRule (.*) app/webroot/$1 [L]

Check URL and stop processing using htaccess

I want to check URL using htaccess. Developer might want run special file - specialfile.php. I use htaccess:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} /specialfile\.php$
RewriteRule .* [L] #don't change adress
RewriteRule ^$ public/index.html [NC,L]
RewriteRule (.*) public/$1 [NC,L]
My idea was: if rewriteCond %{REQUEST_URI} !/specialfile.php$ true than htaccess should use RewriteRule .* [L] - that should mean that specialfile.php will be run and this all. But it doesn't work because it runs next rule: RewriteRule (.*) public/$1 [NC,L].
I think you are using the RewriteCond not correctly. The conditions only affect the next RewriteRule that follows.
Check out the example on the Apache Homepage. Since your 2nd RewriteRule is evalutated, I think your conditions are not correct. To get a litte bit more information about the rewriting, you should increase the log level. This is also documented here.
Your 2nd rule ^$ matches only an empty request btw. That's why it probably does not work as you expect it to.

mod_rewrite troubles

I'm trying to rewrite requests for files that exist, regardless of their extension, in a public directory to that directory, and everything else to a controller. If the user goes to http://example.com/images/foo.gif, and it exists, the image should be served from %{DOCUMENT_ROOT}/public/images/foo.gif. If they go to http://example.com/foo/bar, and it doesn't exist then the request should be routed through index.php. What I have so far is two blocks that work separately, but not together. When both are put in .htaccess, whichever one is first in .htaccess works perfectly, and the one on the bottom is completely ignored (it gives a 404 page when I try to test it). Can someone please explain to me what I'm doing wrong?
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} -f
RewriteRule ^.*$ - [L]
RewriteRule ^.*$ index.php [L]
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} !-f
RewriteRule ^.*$ - [L]
RewriteRule ^(.*)$ %{DOCUMENT_ROOT}/public/$1 [L]
It looks like there's a few things wrong.
It looks like your RewriteCond's are backwards. If %{DOCUMENT_ROOT}/public/%{REQUEST_URI} doesn't exist (!-f) then you want to rewrite to index.php, but if it does exist (-f) then rewrite to /public/$1. The second thing is the RewriteRule ^.*$ - [L] is actually preventing the actual rule from being applied because it ends with [L] and that stops the rewriting in the current iteration.
Even if you remove the ^.*$ - [L] rewrites and flip the -f and !-f, you run into a problem with the 2nd iteration of rewrites:
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} !-f
RewriteRule ^.*$ index.php [L]
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} -f
RewriteRule ^(.*)$ %{DOCUMENT_ROOT}/public/$1 [L]
This is what happens when you try to access http://example.com/foo/bar :
%{DOCUMENT_ROOT}/public//foo/bar doesn't exist, !-f condition met
foo/bar is rewritten to index.php, with [L], end rewrite
The request is INTERNALLY redirected to index.php
With a new URI (index.php) all rules are re-applied
%{DOCUMENT_ROOT}/public/index.php exists, !-f condition failed
%{DOCUMENT_ROOT}/public/index.php exists, -f condition met
index.php gets rewritten to %{DOCUMENT_ROOT}/public/index.php
INTERNAL redirect, and all rules are reapplied to new URI (/public/index.php)
%{DOCUMENT_ROOT}/public//public/index.php doesn't exist, !-f condition met
public/index.php is rewritten to index.php
go back to 3. internal loop
Something similar happens when you try to access http://example.com/images/foo.gif , essentially, you need to get the other rule to stop rewriting the 2nd time around. So you need to add a 2nd set of conditions:
RewriteCond %{REQUEST_URI} !^/public/
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} !-f
RewriteRule ^.*$ index.php [L]
RewriteCond %{REQUEST_URI} !/index.php
RewriteCond %{DOCUMENT_ROOT}/public/%{REQUEST_URI} -f
RewriteRule ^(.*)$ %{DOCUMENT_ROOT}/public/$1 [L]