Wrong GET variable after Mod Rewrite - apache

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]

Related

apache htaccess - rewriting query string with no equal sign

This has been driving me insane. Any help appreciated!
I have a site that has a bunch of dynamic entries in the format:
http://example.com/foo?item-1
http://example.com/foo?item-2
etc.
Noting there is no equal sign / parameter post the ?, just a single string per the above.
I want to clean up the URL's so that we get to:
http://example.com/foo/item-1
So basically, I just want to remove that pesky question mark for foo pages and replace it with a slash. I haven't seen anyone address this case previously. I have tried the following with no success:
RewriteRule ^foo/([^/]*)\$ /foo?$1 [L]
There are two other rules in use already (both of which work). These remove the php from the end of the URL and the http from the start, per the below.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
RewriteCond %{HTTP_HOST} ^www.example.com
RewriteRule (.*) http://example.com/$1 [R=301,L]
Thanks again in advance.
You have a problem with your regex. The \ before the $ symbol means you literally want to match a dollar sign there. Try without the \.
Also, you probably want that before your php rule but after the redirect. You want your external redirects before your internal rewrites:
RewriteCond %{HTTP_HOST} ^www.example.com
RewriteRule (.*) http://example.com/$1 [R=301,L]
RewriteRule ^foo/([^/]*)$ /foo?$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,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]

mod_rewrite seems to ignore [L] flag

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

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]

Multiple RewriteRules for single RewriteCond in .htaccess

I have following command in my .htaccess
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC]
RewriteRule ^(.*?)-([a-z]+) %2/$1.$2 [L]
RewriteRule ^(.*?)-([0-9]+)([a-z]) %2/$1$3.$2 [L]
%2 is not working in second and later lines.
Can I define any variable for %2 and use it in all RewriteRule commands?
Following command works
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC]
RewriteRule ^(.*?)-([a-z]+) %2/$1.$2 [L]
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC]
RewriteRule ^(.*?)-([0-9]+)([a-z]) %2/$1$3.$2 [L]
But I want use %2 for multiple rule line without duplicating condition.
You can use the RewriteRule flag S|skip to tie multiples RewriteRules to a single RewriteCond (or to a set of RewriteConds). Here is an example that applies one Cond to three Rules:
RewriteCond %{HTTP_HOST} !^www.mydomain.com$
# skip rules if NOT within domain - only way to tie multiple rules to one cond
RewriteRule .? - [S=3]
RewriteRule ^path1(/.*)$ /otherpath1$1
RewriteRule ^path2(/.*)$ /otherpath2$1
RewriteRule ^path3(/.*)$ /otherpath3$1
To change an existing Cond to work for multiple Rules you have to:
Negate the condition (prepend it with !)
If you have multiple RewriteConds: Change logical ANDs in the Conds to ORs and vice versa.
Add a skipping rewrite rule in front of all rules that you want to tie to the condition(s). Set the S parameter to the number of Rule lines to be skipped.
Please be aware that it is not possible to use any backreferences that point back to the RewriteCond (like %1) in the skipped Rules. These are only accessible in the skipping RewriteRule.
The variable must be saved as an Apache var, then that can be used without repeated conditions.
Saving in Apache variables are shown in second line. Usage of saved vars in 3th and 4th lines.
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC]
RewriteRule .? - [E=Wa:%1,E=Wb:%2]
RewriteRule ^(.*?)-([a-z]+) %{ENV:Wb}/$1.%{ENV:Wb} [L]
RewriteRule ^(.*?)-([0-9]+)([a-z]) %{ENV:Wb}/$1$3.$2 [L]
Clearly, this isn’t much fun, especially as things grow and become more complex. However, there is a less well known option of the RewriteRule statement, that tells apache to skip the next N number of RewriteRule statement. [S=N]. So instead of checking each time if the request is NOT a file AND is NOT a directory, we could do this:
Code block
#
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [S=3]
RewriteRule ^([^./]+)/$ http://www.santweb.co.uk/$1 [L]
RewriteRule ^([^./]+)/([^./]+)/$ http://www.santweb.co.uk/$1/$2 [L]
RewriteRule ^([^./]+)/([^./]+)/([^./]+)/$ http://www.santweb.co.uk/$1/$2/$3 [L]
#
I found this, from: http://www.sant-media.co.uk/2010/03/applying-rewritecond-to-multiple-rewriterule-in-htaccess/
I think it helpfull
From Apache 2.4 you can use the <If> directive:
RewriteEngine On
<If "%{HTTP:Upgrade} == 'websocket'">
RewriteRule /nidoran/(.*) ws://localhost:8080/nidoran/$1 [P,L]
RewriteRule /kakuna/(.*) ws://localhost:8081/kakuna/$1 [P,L]
RewriteRule /porygon/(.*) ws://localhost:8082/porygon/$1 [P,L]
</If>
http://httpd.apache.org/docs/2.4/expr.html#examples