Apache Rewrite Issue, Custom URI - apache

I have a solution but it is one that I know is not the greatest and would better be solved with a full rewrite. Basically I have 3 rewrites that will go to the correct areas I want and do what they need to do. However in order to switch between where I need to go I had to write a URI class to strip through the url set the page and vars manually. It all works out great but the urls are a pain in the ass specially if not formatted exactly.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/bsiadmin/$ /bsiadmin/index.php [L,QSA]
RewriteRule ^/bsiadmin/(.+)/$ /bsiadmin/index.php?page=$1 [L,QSA]
RewriteRule ^/(.+)/$ /index.php?page=$1 [QSA]
So the first rule will make sure to direct everything to the directory and not the root index.php, the second rule does the same if there is a "page" specified. The last rule will take anything else and make sure it uses the root index.php and goes from there.
Example of urls:
http://mysite.test/icecream/id=2/
My custom uri class would strip this clean and set id as a $_REQUEST var.
I guess what I really want to know is how can I just rewrite a simple url such as:
http://mysite.test?page=icecream&id=2
AS
http://mysite.test/icecream/id/2/
Without any limitation on how many vars can be passed and the directory that does exist "bsiadmin" to display without me having to use a uri class to direct it.
Thanks for the help.

You can use mod_rewrite to do so:
RewriteRule ^/([^/]+)/([^/]+)/([^/]+)/(.*) /$1/$4?$2=$3 [N,QSA]
RewriteRule ^/([^/]+)/$ /bsiadmin/index.php?page=$1 [L,QSA]
But I think the best would be to use PHP for that job. Because with mod_rewrite you can only rewrite a fixed amount of URL arguments at a time (here one with every rewrite). With PHP you can parse any arbitrary number of arguments like this:
$_SERVER['REQUEST_URI_PATH'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$segments = explode('/', $_SERVER['REQUEST_URI_PATH']);
if (count($segments) > 2) {
for ($i=4, $n=count($segments); $i<$n; $i+=2) {
$_GET[rawurledecode($segments[$i-1])] = rawurldecode($segments[$i]);
}
$_GET['page'] = rawurldecode($segments[1]);
}
Then all you need for mod_rewrite is this single rule to rewrite the requests to your index.php:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !^/bsiadmin/index\.php$ /bsiadmin/index.php [L]

Related

adding forward slash after rewrite if not exist

I have read a ton and tried a ton of solutions, but can't find one specific to my needs.
In my htaccess I have the following:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^profile/([\w-]+)/?$ profile.php?username=$1 [L,QSA]
RewriteRule ^profile/([\w-]+)/([\w-]+)/?$ profile.php?username=$1&type=$2 [L,QSA]
RewriteRule ^profile/([\w-]+)/([\w-]+)/([\w-]+)/?$ profile.php?username=$1&type=$2&o=$3 [L,QSA]
This works wonderfully, except for 1 small problem.
If profile/username does not have a / the links on the page will break unless absolute urls are used. So <a href="./1"> will end up as profile/1 instead of profile/username/1
If I change the first rewrite to the following:
RewriteRule ^profile/([\w-]+)/$ profile.php?username=$1 [L,QSA]
then https://myurl/username will return a 404 which I do not want - I want it to force the / on the end if it does not exist.
I have tried adding:
RewriteRule ^(.*)([^/])$ /$1$2/ [L,R=301]
I have tried
RewriteRule ^(.*)$ https://nmyurl/$1/ [L,R=301]
Just can't figure out how to do this with the rewrite conditions already in place.
To add an optional traling at the end of your profile URLs you can use this
RewriteEngine on
RewriteCond %{REQUEST_URI} ^/profile/
RewriteRule !/$ %{REQUEST_URI}/ [L,R]
If profile/username does not have a / the links on the page will break unless absolute urls are used. So <a href="./1"> will end up as profile/1 instead of profile/username/1
If the issue only applies to URLs of the form /profile/<username> then I would be specific and only append the trailing slash to these specific URLs, otherwise, you are going to get a lot of redirects (which could be detrimental to SEO).
However, you should ensure that internal links are for the canonical URL (ie. with the trailing slash).
For example, the following should go before your existing rewrites:
RewriteRule ^(profile/[\w-]+)$ /$1/ [R=301,L]
Since the canonical URL requires a trailing slash this should be a 301 (permanent) redirect. (But test with a 302 first.)
Alternatively, instead of redirecting in .htaccess (if you are still linking to the slashless URL internally) then you could add a base element (that includes the trailing slash) to the head section to state what relative URLs should be relative to.
For example:
<base href="/profile/<username>/">
Aside:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^profile/([\w-]+)/?$ profile.php?username=$1 [L,QSA]
RewriteRule ^profile/([\w-]+)/([\w-]+)/?$ profile.php?username=$1&type=$2 [L,QSA]
RewriteRule ^profile/([\w-]+)/([\w-]+)/([\w-]+)/?$ profile.php?username=$1&type=$2&o=$3 [L,QSA]
The two RewriteCond directives would seem to be entirely superfluous. RewriteCond directives only apply to the first RewriteRule that follows. But the RewriteRule patterns are unlikely to match real files anyway (unless you have files without extensions or directories with the same name).
So unfortunately I wasn't able to actually achieve what I was hoping to because there are multiple levels of variables that may or may not exist.
In part I used the solution provided by Amit:
RewriteCond %{REQUEST_URI} ^/profile/
RewriteRule !/$ %{REQUEST_URI}/ [L,R]
However this wasn't enough, because as pointed out by MrWhite there are 3 separate potential url's.
https://myurl/profile/username/
https://myurl/profile/username/type/
https://myurl/profile/username/type/o/
In this sitation username should always exist, but type and o may or may not exist.
So what I did was detect the level of the url and then created conditional . and .. using php.
The variable o is always numeric and variable type is never numeric so this worked for me.
if (isset($_GET['o'])) { $o = strip_tags($_GET['o']); }
elseif (isset($_GET['type']) && is_numeric($_GET['type'])) { $o = strip_tags($_GET['type']); }
Then I detect:
// if o is set or if type is numberic, use ..
if (isset($_GET['o']) || (isset($_GET['type']) && is_numeric($_GET['type']))) {
$dots = '..';
// if o is not set and type is not numeric just use .
} else {
$dots = '.';
}
end result of 1:
if url is https://myurl/profile/username/
result is https://myurl/profile/username/1/
if url is https://myurl/profile/username/3/
result is https://myurl/profile/username/1/
if url is https://myurl/profile/username/type/3/
result is https://myurl/profile/username/type/1/
Which was the desired outcome.

mod_rewrite: Redirect to page named in GET variable

I'm changing the directory/URL structure on a site to make it more legible (and, of course, pretty), and I've run into a snag. Let's say I have the following URI request:
http://example.com/page.php?foo=bar&baz=qux
If someone has bookmarked the old URI, I want to redirect that request in .htaccess to a page determined by the foo variable, like so:
http://example.com/bar
Is this possible? If so, how can I do it?
Edit
Here's a more concrete example. I used to have a page called cc.php. It was called with two parameters: curriculum and title. So, a query might look like cc.php?curriculum=lesson1&title=First%20Steps. This is obviously very ugly, so I want to redirect it to /lesson1
I removed the old cc.php and created a new index.php that figures out which curriculum to display based on the request URI. If a visitor goes to /lesson1, it automatically knows what to do via the following rules:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?rt=$1 [L,QSA]`
There's a chance someone has bookmarked the old cc.php scheme, however, so I want to be able to rewrite to the new scheme if someone does that. I've tried the following rule:
RewriteCond %{QUERY_STRING} ^curriculum=(.*)&title=(.*)$
RewriteRule ^cc.php$ %1
While this will correctly redirect cc.php?curriculum=lesson1&title=some_title to /lesson1, it will not work in conjunction with the previous rule.
How can I get both sets of rules working? The other solution I've thought of is to have a cc.php that calls header() based on $_GET['curriculum'], but that seems ugly.
You need to use a Redirect flag [R] in you rule to externally redirect the request from old URL format to the new one.
RewriteCond ℅{QUERY _STRING} ^curriculum=([^&]+)&title=(.*)$
RewriteRule ^cc.php$ %1? [L,R]
RewriteCond {REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?rt=$1 [L,QSA]

How to retrieve, if mod_rewrite should rewrite?

I'm trying to do a mod_rewrite. The given URLS should look like this:
RewriteRule ^([a-zA-Z0-9\-)/([a-zA-Z0-9\-)$ index.php?anum=$1&aname=$2
The problem with that is, that it also rewrites things like javascript files matching the pattern, because they are in some subdirectories.
So how do I achieve, that mod-rewrite only accepts URLs like "foo/bar" and no URL like "fizz/buzz/jq.js"?
if you want
test/foo/dir/test44.j4s
to
dir test44.j4s
RewriteRule ^foo/(.*)/(.*) index.php?dir=$1&file=$2 [L]
You may ensure to not match existing files or directories by using the following rewrite conditions:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9\-)/([a-zA-Z0-9\-)$ index.php?anum=$1&aname=$2
See http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritecond for the full power of RewriteCond.
I often use
RewriteCond $1 !^favicon.ico|^css|^js
to exclude favicon, and the css and js directories from beeing rewritten.
The following should exclude Javascript and CSS files:
RewriteCond $1 !\.css$|\.js$
Just do this thing: if the request doesn't end with a known extension, then apply the rewrite rule:
RewriteCond %{REQUEST_URI} (.*)(\.(css|js|pdf|jpg|jpeg|gif|png|ico))
RewriteRule ^([a-zA-Z0-9\-)/([a-zA-Z0-9\-)$ index.php?anum=$1&aname=$2
Or do it the opposite side: if the request starts with a known extension, then stops, otherwise keep on applying the rest:
RewriteRule (.*)(\.(css|js|htc|pdf|jpg|jpeg|gif|png|ico)){1}$ $1$2 [QSA,L]
RewriteRule ^([a-zA-Z0-9\-)/([a-zA-Z0-9\-)$ index.php?anum=$1&aname=$2
Both solutions should work.
Tell me which one did ;)
Olivier

Help with dynamic RewriteRule in .htaccess

I would like to use RewriteRules in .htaccess to achieve particular url rewriting, but can't get it right, can somoene help?
I'm trying to rewrite the path (everything between the first and last /) into one query string, and the filename into another
e.g:
http://domain.com/users/admins/testuser.html
rewrites to
http://domain.com/index.php?path=users/admins&file=testuser
and
http://domain.com/home.html
rewrites to
http://domain.com/index.php?path=&file=home
and
http://domain.com/settings/account.html
rewrites to
http://domain.com/index.php?path=settings&file=account
EDIT:
Many thanks to the first two answerers, they are both good answers however I can't figure which one to use!
Is there any benefit to parsing the path from php itself, or vice-versa?
Try this rule:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^((.+)/)?([^/]+)\.[^/.]+$ index.php?path=$2&file=$3
But it may be easier to use PHP’s parse_url and pathinfo for that:
$_SERVER['REQUEST_URI_PATH'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$pathinfo = pathinfo(substr($_SERVER['REQUEST_URI_PATH'], 1));
var_dump($pathinfo);
Now you just need this rule to rewrite the request to the index.php:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule !^index.php$ index.php
Try this:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^((\w+/)*)(\w+)\.html$ index.php?path=$1&file=$3

.htaccess rewrite rule to add a string in the middle of the URL

Using a .htaccess rewrite rule, I need to add "?q=" before the path on any URL's containing the word "imagecache"
Therefore, if the URL is:
http://mysite.com/sites/default/files/imagecache/myimage.jpg
...then it will really try:
http://mysite.com/?q=sites/default/files/imagecache/myimage.jpg
But that will ONLY happen if the URL contains "imagecache." Otherwise, it does no rewriting.
Also, this will only happen if /sites/default/files/imagecache/myimage.jpg isn't already an existing image file. I believe I can do that using:
RewriteCond %{REQUEST_FILENAME} !-f
...right? It's just the first part that I can't figure out.
Something like this?:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*imagecache.*)$ /?q=$1 [L,QSA]
RewriteRule ^mysite.com/(.*)/imagecache/(.*)$ mysite.com/?q=$1/imagecache/$2