Mod_Rewrite change GET value pairs into nice slashes - .htaccess

I have a url http://domain.com/module/controller/action/get1/value1/?get2=value2&get3=value3 I want to use Mod_Rewrite to change the ?&= to appropriate / slashes inline with the first GET variable.
I also need to avoid conflicts with my current Mod_Rewrite rules:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ zend.php [NC,L]

If you don't need the GET parameters in their native $_GET container, you could leave the htaccess file as is, and have the front controller (I assume you are using PHP) do the parsing and not mod_rewrite.
You would have to access the $_SERVER["REQUEST_URI"] and parse out the parameters before any other URL parsing (I think you are using the Zend Frameworks') kicks in.
This works only if you can modify the part of your program that needs value1 value2 value3 etc. If it's some Zend component that needs the parameters, and that component fetches them directly from $_GET, this will not work for you and you may in fact have to build mod_rewrite instructions.
Btw, you are aware by the way that with the method in your .htaccess, invalid links to images, CSS files and other media types will all be routed to zend.php?

Try putting these rules in front of yours:
RewriteCond %{QUERY_STRING} ^([^&=]+)=([^&]+)&(.+)
RewriteRule ^ %{REQUEST_URI}%1/%2/?%3 [N]
RewriteCond %{QUERY_STRING} ^([^&=]+)=([^&]+)$
RewriteRule ^ %{REQUEST_URI}%1/%2/? [L,R]

I could not find a nice way to do this... so I resolved to write some minimised PHP code at the top of my zend.php.
list($sURL, $sQuery) = explode('?', $_SERVER['REQUEST_URI']);
$sOriginalURL = $sURL;
if ('/' !== substr($sURL, -1)) $sURL .= '/';
if (isset($sQuery)) {
foreach (explode('&', $sQuery) as $sPair) {
if (empty($sPair)) continue;
list($sKey, $sValue) = explode('=', $sPair);
$sURL .= $sKey . '/' . $sValue . '/';
}
}
if (isset($sQuery) || $sOriginalURL !== $sURL) header(sprintf('Location: %s', $sURL));
If anyone can improve on this please comment below.

Related

.htaccess - changing urls - RewriteRule not working

I'm fairly new to this, hopefully you can help me fix this problem.
I have a URL structure that gets an id from a database and shows the url like this:
www.website.com/post.php?P=18
I would like to present the URL's as:
www.website.com/post/18
In my .htaccess file, I've altered it like so:
RewriteEngine on
RewriteRule ^post/(\w+)$ post.php?P=$1
I've read through a few posts about this here on SO but I can't seem to figure it out.
I followed this:
The Rule:
RewriteRule ^user/(\w+)/?$ user.php?id=$1
Pattern to Match:
^ Beginning of Input
user/ The REQUEST_URI starts with the literal string "user/"
(\w+) Capture any word characters, put in $1
/? Optional trailing slash "/"
$ End of Input
Substitute with:
user.php?id= Literal string to use.
$1 The first (capture) noted above.
Thank you!
Try with below,
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^post/(\w+)$ post.php?P=$1 [L]
I think it's important to share this kind of information for others that may be having the same issue so here goes.
The Problem:
[1] A link that looked like: www.example.com/news.php?P=1
The link should look like www.example.com/news/1
Then, the link would have to end up showing text instead of an ID. www.example.com/news/news-name
The solution
First, i had anchor tags that looked like this
It gave the first result in the URL [1]. To change it so it would present as a www.example.com/news/1, i had to do the following:
Create a htaccess file and fill it like this:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [NC]
### THIS IS AN EXAMPLE FOR MULTIPLE EXPRESSIONS ###
#RewriteRule ^news/([0-9]+)/([0-9a-zA-Z_-]+) news.php?P=$1&name=$2 [NC,L]
RewriteRule ^news/([0-9]+) news.php?P=$1 [NC,L]
And then, change the anchor tags to:
[1] Would be done now.
The challenge now was to use a slug instead of an ID. On the post creation page, I added the following PHP:
<?php
setlocale(LC_ALL, 'en_US.UTF8');
function slugit($str, $replace=array(), $delimiter='-') {
if ( !empty($replace) ) {
$str = str_replace((array)$replace, ' ', $str);
}
$clean = iconv('UTF-8', 'ASCII//TRANSLIT', $str);
$clean = preg_replace("/[^a-zA-Z0-9\/_|+ -]/", '', $clean);
$clean = strtolower(trim($clean, '-'));
$clean = preg_replace("/[\/_|+ -]+/", $delimiter, $clean);
return $clean;
}
?>
Then, on the news insertion page, I added: $slug = slugit("$entry1");, which would pass $entry1 = $_POST['title']; as the title of the page, but slugified. On the database for the news, i created a column to accommodate $slug as a permalink name.
Now to present the URL with the slug, I had to change the anchor tag to:
And on the htaccess, change RewriteRule ^news/([0-9]+) news.php?P=$1 [NC,L] to RewriteRule ^news/([0-9a-zA-Z_-]+) news.php?P=$1 [NC,L]
That's how I got it to work. I hope this will help people with similar problems fix theirs.

Is there a way to add IF to .htaccess?

I'm building a web-builder, but I have no idea how to use .htaccess. Is there a way to make something like that (I'll write it in php because my English bad so it is easier to me):
<?php
if(ERROR && ONE_NAME){ //There is no page like that, for example: www.example.com/thispageisntexists
//redirect to page /website.php?webname=thispageisntexists
}elseif(ERROR && TWO_OR_MORE){ //for example: www.example.com/thispageisntexists/hi.php
//redirect to page /website.php?webname=thispageisntexists&pagename=hi
//if url is www.example.com/thispageisntexists/hi.php#hi rediract to /website.php?webname=thispageisntexists&pagename=hi#hi
//if url is www.example.com/thispageisntexists/hi.php?name=vlad&last=gincher rediract to /website.php?webname=thispageisntexists&pagename=hi$name=vlad&last=gincher
//and so on...
}
?>
inside website.php I'll check if the page exist and if no it will redirect to 404.php, and if it exist it will show the website.
I find it somewhat unclear what url's should be redirected or rewritten. I am assuming you have (or want) seo urls like this http://example.com/aaa and http://example.com/aaa/bbb and want to internally rewrite that to something that makes sense to the server.
You want it to work if there is already a query string present. This means that you have to concat the two query strings with the [QSA] flag.
"There is no page like that" translates to a condition that checks if the requested filename exists. You can negate that condition with a ! before the second argument. That would be RewriteCond %{REQUEST_FILENAME} !-f.
#hi is an anchor. It is never sent to the server. Your browser should however automatically keep appending that to the url, even when redirecting the request.
I see that Jon Lin already posted a .htaccess file that should work for you. I will still post this answer to clarify what is used there.
Try:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/website\.php
RewriteRule ^([^/]+)$ /website.php?webname=$1 [L,R,QSA]
# you can remove the "R" if you don't want to redirect the browser
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/website\.php
RewriteRule ^([^/]+)/(.*?)(\.php)?$ /website.php?webname=$1&pagename=$2 [L,R,QSA]
# you can remove the "R" if you don't want to redirect the browser
There's nothing with such an explicit IF-THEN-ELSE. .htaccess does have ways of doing conditional URL/URI redirects and rewrites, using RewriteCond and RewriteRule commands. They use "regular expressions" to match patterns, so they aren't as flexible as a real scripting language like PHP, but you can do lots with them.
RewriteEngine On
RewriteCond %{REQUEST_FILE} !-f
RewriteRule /?(.*)$ /website.php?webname=$1 [L]
might be a start for what I understand you're trying to do.

How to rewrite URLS to remove index.php AND force lowercase with codeigniter?

I’m having trouble doing both of these at once with .htaccess.
Removing index.php is working, but trying to add the force lowercase keeps throwing a 500 error. I am using codeigniter. Any help is appreciated!
here’s my .htaccess code:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?$1
RewriteMap lc int:tolower
RewriteCond %{REQUEST_URI} [A-Z]
RewriteRule (.*) ${lc:$1} [R=301,L]
The php code suggested does not work if you are just trying to force to lowercase. Below is the correct php code.
$url = $_SERVER['REQUEST_URI'];
$pattern = '/([A-Z]+)/';
if(preg_match($pattern, $url)) {
$new_url = strtolower($url);
Header( 'HTTP/1.1 301 Moved Permanently' );
Header( 'Location: ' . $new_url );
exit;
}
// your code here
Discussion Found Here Force lowercase .htaccess
That is because it is impossible, let me explain.
Removing the index.php still sends all requests to index.php/yadda/yadda so the requested uri still has the index.php in it even though you do not see it or add it into the url.
The redirect (triggered by the 2nd rewrite rule) nulls the index section so you would then get mysite.com/index.php/lowercase/
BUT beyond all this RewriteMap can only be declared in your Httpd.conf file, you would declare it there with:
RewriteMap lc int:tolower
Then in your .htaccess file use your variable lc, but then again only one of the two will win you can't have both.
You will either get the URL lowercase or have the site working without using the index.php because they are always going to conflict because of the nature of what each one does.
Really the only way I can see it happening is in php, like so:
$this->load->helper('url');
$your_URL = uri_string();
preg_match_all('/[A-Z]/', $your_URL, $match) ;
$total_count = count($match [0]);
if($total_count > 0){
$new_line = strtolower($your_URL);
redirect($new_line);
}
This I would put into the main construct of your classes, I hope this helps you.

Multiple mod rewrites in .htaccess

I want the following rules but I don't seem to get the right setup.
<domain>/training-courses/
both with or without the slash at the end it should go to:
<domain>/?index.php?page=training-courses
and for each variable extra after this I want it to behave like this:
<domain>/training-courses/success/another-value/and-yet-another/
to
<domain>/?index.php?page=training-courses&val1=success&val2=another-value&val3=and-yet-another-value
If it's not possible to have the option for unlimited leading variables, i'd like to have at least 2 variables after the page variable
Is this possible? and how do I get this sorted out?
I have this so far:
RewriteEngine On
RewriteRule ^test/([^/]*)/$ /test/index.php?pagina=$1&val1=$2
RewriteRule ^test/([^/]*)$ /test/index.php?pagina=$1&val1=$2
RewriteRule ^test/([^/]*)/([^/]*)/$ /test/index.php?pagina=$1&val1=$2
RewriteRule ^test/([^/]*)/([^/]*)$ /test/index.php?pagina=$1&val1=$2
RewriteRule ^test/([^/]*)/([^/]*)/([^/]*)/$ /test/index.php?pagina=$1&val1=$2&val2=$3
RewriteRule ^test/([^/]*)/([^/]*)/([^/]*)$ /test/index.php?pagina=$1&val1=$2&val2=$3
You're quite correct, you can't handle unlimited variables as sub-folders in the way you would like to, mod_rewrite just doesn't support it.
However, in answer to your first problem, to make the trailing / optional, change your rules to the following:
RewriteRule ^test/([^/]*)/?$ /test/index.php?pagina=$1
RewriteRule ^test/([^/]*)/([^/]*)/?$ /test/index.php?pagina=$1&val1=$2
RewriteRule ^test/([^/]*)/([^/]*)/([^/]*)/?$ /test/index.php?pagina=$1&val1=$2&val2=$3
After hours and hours of struggeling it just WON'T work. Since unlimited variables just isn't supported I'm ok with two variables besides the page (pagina). What I have now is this:
RewriteEngine On
RewriteRule ^(test/images|test/css|test/js|test/lib)($|/) - [L]
RewriteRule ^test/(.*)/(.*)/(.*)/ test/index.php?pagina=$1&e=$2&t=$3
RewriteRule ^test/(.*)/(.*)/(.*) test/index.php?pagina=$1&e=$2&t=$3
RewriteRule ^test/(.*)/(.*)/ test/index.php?pagina=$1&e=$2
RewriteRule ^test/(.*)/(.*) test/index.php?pagina=$1&e=$2
RewriteRule ^test/(.*)/ test/index.php?pagina=$1
RewriteRule ^test/(.*) test/index.php?pagina=$1
None of the RewriteRules work except from the first one which excludes folders from the other rewriterules. With working I mean it will actually redirect the URLs to index.php but I can't get the values in PHP through ie $_GET['pagina']
Strange thing is this WILL work, though is way too limited for my liking:
RewriteEngine On
RewriteRule ^(test/images|test/css|test/js|test/lib)($|/) - [L]
RewriteRule ^test/(.*)/ test/index.php?pagina=$1
I would like to come back to my own question and answer it. For a while now i'm using exactly what I want.
First off I have this in my htaccess:
<IfModule mod_rewrite.c>
RewriteEngine On
# existing folders or files
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.* - [L]
# non-existing folders or files
RewriteRule ^(.*)/? index.php?p=$1 [L]
</IfModule>
This allows to access real files at all times. If the path does not exist it will pass the entire path to index.php.
In index.php I have the following code at the top:
// Split the URL in all its components
$request = parse_url(strip_tags($_GET['p']));
// Only take the path without any leading slashes
$path = rtrim($request['path'], '/');
// Split each variable from eachother
$path_array = explode("/", $path);
if(count($path_array)){
// By default I select the first variable as the page I will include later on
$page = $path_array[0];
// Each other variable I will pass as $val[#nr] starting with 1. Just to be sure I'm going to strip tags and slashes
for($i=1;$i<count($path_array);$i++){
$path_array[$i] = strip_tags($path_array[$i]);
$path_array[$i] = stripslashes($path_array[$i]);
$val[$i] = $path_array[$i];
}
}
else{
// If $path_array doesn't contain multiple variables lets assume $path will contain the $page
$page = $path;
}
// If $page is empty let's take on the default page to include later on
if(empty($page)){
$page = "home";
}
if(file_exists($page . ".php")){
include($page . ".php");
}
else{
echo "Page " . $page . ".php does not exist.";
}
To give you some examples of the posibilities:
domain.com/ will include home.php
domain.com/product/ will include product.php
domain.com/product/298833/ will include product.php and parse 298833 as $val[1];
domain.com/product/298833/purple-candle will include product.php and parse 298833 as $val[1]; and purple-candle as $val[2]; Notice the leading slash not being included. It does not matter.
I'm open for improvement or tips in general. For now this is definitely the answer I was looking for back when I asked this question.

How to pass-through requests not going to specific directories to index.php/path?

In my present project I've got several directories: application (my MVC files, which mustn't be accessed), images, css, and js. Effectively I want all requests to images/css/js to proceed unchanged, but all others I wish to call index.php/my/path.
My .htaccess currently looks like this, and is wreaking havoc with my routing.
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond $1 !^(index\.php|images|js|css|robots\.txt)
RewriteRule ^(.*)$ http://example.com/index.php/$1 [L]
</IfModule>
This isn't working as relative URLs start stacking up, such as: example.com/blog/view/1/blog/view/2.
When I attempt something like,--
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/(index\.php|images|js|css|robots\.txt)
RewriteRule ^ index.php%{REQUEST_URI} [PT]
</IfModule>
I get this error with any request: No input file specified.
How can I force all requests not to my whitelisted directories to call, not redirect to (redirection murders posting, I found), index.php/path? IE, when /blog/view/1 is requested by the browser, .htaccess calls index.php/blog/view/1. The reference files at Apache's site aren't too clear about how to do this sort of thing—that, or, I am just missing the point of what I'm reading about RewriteRule.
And, I really want to understand this. Why will your answer work? Why are my attempts failing?
This is what I have in my .htaccess for my framework:
<IfModule mod_rewrite.c>
RewriteEngine on
#This will stop processing if it's images
RewriteRule \.(css|jpe?g|gif|png|js)$ - [L]
#Redirect everything to apache
#If the requested filename isn’t a file….
RewriteCond %{REQUEST_FILENAME} !-f
#and it isn’t a folder…
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?$1 [L,QSA]
#L = (last - stop processing rules)
#QSA = (append query string from requeste to substring URL)
</IfModule>
Hope this helps.
PS: Maybe you want to remove the lines to stop redirecting if it's a file or folder ;)
Antonio helped me get on the right track, so here's the resulting .htaccess:
<IfModule mod_rewrite.c>
RewriteEngine on
# skip if whitelisted directory
RewriteRule ^(images|css|js|robots\.txt|index\.php) - [L]
# rewrite everything else to index.php/uri
RewriteRule . index.php%{ENV:REQUEST_URI} [NE,L]
</IfModule>
You're going to have to do that using PHP. For example, if you wanted to split your URI into something like domain.tld/controller/action/param, then you could use the following PHP code as a start:
<?php
// Filter URI data from full path
$uri_string = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['REQUEST_URI']);
$uri_string = trim($uri_string, '/'); // Make sure we don't get empty array elements
// Retrieve URI data
$uri_data = explode('/', $uri_string);
In that case, $uri_data[0] is the controller, $uri_data[1] is the action, and beyond that are parameters. Note that this isn't a foolproof method, and it's never a great idea to trust user-entered input like this, so you should whitelist those controllers and actions which can be used.
From here, knowing the controller and having a consistent directory structure, you can require_once the proper controller and call the action using variable variables.
This is what I use in my .htaccess file for my CMS:
Options -Indexes
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ index.php/$1 [NC,L]
And then in my index.php file I have:
$path_info = '';
$path_info = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : $path_info;
$path_info = isset($_SERVER['ORIG_PATH_INFO']) ? $_SERVER['ORIG_PATH_INFO'] : $path_info;
$request = explode('/', trim($path_info, '/'));
// if $request[0] is set, it's the controller
// if $request[1] is set, it's the action
// all other $request indexes are parameters
Hope this helps.

Resources